|
18183
|
789
|
21
|
2026-05-11T11:02:00.008564+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-11/1778 /Users/lukas/.screenpipe/data/data/2026-05-11/1778497320008_m2.jpg...
|
PhpStorm
|
faVsco.js – Client.php
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
JY-20725-handle-HS-search Project: faVsco.js, menu
JY-20725-handle-HS-search-rate-limit, menu...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"bounds":{"left":0.025930852,"top":0.019952115,"width":0.03856383,"height":0.025538707},"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JY-20725-handle-HS-search-rate-limit, menu","depth":5,"bounds":{"left":0.064494684,"top":0.019952115,"width":0.09541223,"height":0.025538707},"on_screen":true,"help_text":"Git Branch: JY-20725-handle-HS-search-rate-limit","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
-5641617897080429754
|
-8160223333407913180
|
visual_change
|
hybrid
|
NULL
|
Project: faVsco.js, menu
JY-20725-handle-HS-search Project: faVsco.js, menu
JY-20725-handle-HS-search-rate-limit, menu
PhostormVIewINavicareCodeFV faVsco.js°9 JY-20725-handle-HS-search-rate-limProiect© BatchSyncCollectolhuospot/service.onpT SyncCrmEntitiesTrait.phpc) CachedCrmServiceDecorator.ong•JiminnyDeougcommane.onge balchsynckealsseo closeaDealstagess)MatchactivityermData.pngDealrielasservice.gc)Decorateacuivilv.or© FieldDefinitions.phrC) FieldT vpeconvertee Hubspotclientinterc) Hubspotlokenman© PayloadBuilder.phpC) RemotecrmobiectrP ResponseNormalizec) Service.onrC)SvncFieldAction.onC) SvncRelatedActivitC) WebhookSvncBatclv MintearationAorM AcceccorsConfigDDTO• M SiltersD JobsM ProspectSearchStrW service lraits© DataClient.php© DecorateActivity.phLocalSearch.pheu Localsearchinteria© RemoteSearch.phpc) Service.phpv D Listeners© ConvertLeadActivitc) PurceLookuocache> M Metadata> MiarationPioedrivev Salesforce• D FieldsM OnnortunitvMatcheMOnnortunitvSvneStM ProsneetSearchStr.• M ServiceTraitcC) Client nhr© DecorateActivity.ph. Delete@biectsTrait© FieldDefinitions.php© PayloadBuilder.php© Profile.php© QueryBuilder.phpo RematchActivityonCrmObiectDetach.pngRateLimitexcepuioC) CrmObiectsResolver.pho() ProviderRateLimiter.phpC) PaqinationContia.phpclass Client extends BaseClient implements HubspotClientinterface253 0255259261|//277278281290• @param array<string, mixed> $payload The search payload with filters, sorts, properties, etc* dreturn arrau The search response with 'results'.'paging' keys* Othrows RateLimitException When rate limit is hit* @throws HubspotException On API errorspublic function search(string SobjectType, array $payload): arraySendnoint = solf••RASE IIRI-|return Sthis->executeRequest(function ( use (Sendpoint, $payload) {Sresponse = $this->getInstance()->getClient()->request( method: 'POST', $endpoint, ['json' => $payload]):return Sresponse->toArrayO:}):* dchrows DealAp1Lxceptzon* ochrows crmexcentzonpublic function oetOpoortunitvßvld(strina Scrmid. array Sfields): arravtry{Sdeal = sthis->executeRequestfn0 => Sthis->aetNewinstanceO->crmO->dealsO->basicApiO->qetBvldoSdeal = sthis->aetNewiinstance@->crmo->dealso->hasi.canto->aetBvitdimolode @ separator: " ", Sfields)} catch (DealAniFycention Se) $Sthis->loa->info@*Hubsnot Failed to fetch onnortunity'.?'crm_id' => $crmId,'reason' => $e->getMessage)thnow So.if (! Sdeal instanceof DealWithAssociations) {throw new Crmexceptionm:'Deal not found'):A6 VA.4 HS_local (iminny@localhost]# console [PKoD)A console (EU]A console [STAGING][2026-05-07 14:21:15] local.INF0: [Hubspot] DEBUG Getting headers {w19A"Vace".L"Inu,or May 2020 14.21.10 bMl"J"CF-Ray":"9t80deb8dbo0dcsa-S0F".."Strict-Transport-Security":"max-aqe=31536000* 1ncLudesubDomains: preload")."server-timing": ["hcid;desc=|"019e02d0-6fd8-7812-bdba-885b7ccb3ee3)","x-hubspot-correlation-id":["019e02d0-6fd8-7812-bdba-885b7ccb3ee3"],"Set-Cookie":["__cf_bm=StUrtdQgXVrik50pdqF6hZVYKhzTnQBidvMabeCtm0Y-1778163675-[IP_ADDRESS]-rI.ZggtDKxTg07-May-26 14:51:15 GMT; domain=.hubapi.com; Http0nly; Secure; SameSite=None"],\"url\":"https:\/\\/a.nel.cloudflare.com\\/report\\/v4?s=NYAlsVTP0fYm32qrSDjxYE4sd2RWRqiSp3wHsmdEgZ\"group\":\"cf-nel\",\"max_age\":604800}"],"NEL".L"1\"success_fraction\":0.01,("max agel ":604800}"]"Server": ["cloudflare"]}} {"trace_1d":"C/ab8565-905t-4604-9405-0e5b551e5545'5CascadeHubspot Rate LimitThought for 5s ›Read Client.php #L86-200b Client.phpVerify nothing broke:Command docker. tailhlPreparation tor kerl…. In oym100% Lz• Mon 11 May 14:01:59HandleHubspotRateLimitTest v+0 ..lets simplify it, just call it parseMessage and skip policy al together+17 -35I Il 1III111 1Ask anvthina (84L)Claude Onus 4.7 MediumTects naccod. 12 /50 mlWN Windsurf Toams 250-1UTF.8io 4 spaces...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
18184
|
788
|
23
|
2026-05-11T11:02:31.451928+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-11/1778 /Users/lukas/.screenpipe/data/data/2026-05-11/1778497351451_m1.jpg...
|
PhpStorm
|
faVsco.js – Client.php
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
JY-20725-handle-HS-search Project: faVsco.js, menu
JY-20725-handle-HS-search-rate-limit, menu
Start Listening for PHP Debug Connections
HandleHubspotRateLimitTest
Run 'HandleHubspotRateLimitTest'
Debug 'HandleHubspotRateLimitTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JY-20725-handle-HS-search-rate-limit, menu","depth":5,"on_screen":true,"help_text":"Git Branch: JY-20725-handle-HS-search-rate-limit","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"HandleHubspotRateLimitTest","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'HandleHubspotRateLimitTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'HandleHubspotRateLimitTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
-3367139436975185842
|
-8780819654538876030
|
idle
|
hybrid
|
NULL
|
Project: faVsco.js, menu
JY-20725-handle-HS-search Project: faVsco.js, menu
JY-20725-handle-HS-search-rate-limit, menu
Start Listening for PHP Debug Connections
HandleHubspotRateLimitTest
Run 'HandleHubspotRateLimitTest'
Debug 'HandleHubspotRateLimitTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
SlackFileEditViewGoHistoryWindowHelpDOCKER181DEV (docker)$2APP (-zsh)883viewsjiminny-worker-processing-2:jiminny-worker-processing-2_00: stoppedjiminny-worker-processing-3:jiminny-worker-processing-3_00: stoppedjiminny-worker-processing-4: jiminny-worker-processing-4_00: stoppedJiminny-worker-processing-5:j1minny-worker-processing-5_00:jiminny-worker-processing-delayed: jiminny-worker-processing-delayed_00: stoppedworker-analytics:worker-analytics_00: stoppedworker-conferences:worker-conferences_00: stoppedworker-crm-update:worker-crm-update_00: stoppedworker-download:worker-download_00: stoppedworker-emails:worker-emails_00: stoppedworker-nudges:worker-nudges_00: stoppedworker:worker_00: stoppedworker-audio:worker-audio_00: stoppedworker-calendar:worker-calendar_00: stoppedworker-crm-sync:worker-crm-sync_00: stoppedartisan-schedule:artisan-schedule_00: stoppedworker-es-update:worker-es-update_00: stoppedjiminny-worker-processing-1:jiminny-worker-processing-1_00: stoppedartisan-schedule:artisan-schedule_00: startedjiminny-worker-processing-1:jiminny-worker-processing-1_00: startedjiminny-worker-processing-2:jiminny-worker-processing-2_00: startedjiminny-worker-processing-3:jiminny-worker-processing-3_00: startedjiminny-worker-processing-4:jiminny-worker-processing-4_00: startedjiminny-worker-processing-5:jiminny-worker-processing-5_00:jiminny-worker-processing-delayed: jiminny-worker-processing-delayed_00: startedworker:worker_00: startedworker-analytics:worker-analytics_00: startedworker-audio:worker-audio_00: startedworker-calendar:worker-calendar_00: startedworker-conferences:worker-conferences_00: startedworker-crm-sync:worker-crm-sync_00: startedworker-crm-update:worker-crm-update_00: startedworker-download:worker-download_00: startedworker-emails:worker-emails_00: startedworker-es-update:worker-es-update_00: startedworker-nudges:worker-nudges_00: startedroot@docker_lamp_1:/home/jiminny#php artisan jiminny:debugDispatching 100 MatchActivityCrmData jobs (portal=2)Done. Watch logs and run jiminny:debug observeRateLimit to inspect cache state.root@docker_lamp_1:/home/jiminny# php artisan jiminny:debugDispatching 100 MatchActivityCrmData jobs (portal=2)Done. Watch logs and run jiminny:debug observeRateLimit to inspect cache state.root@docker_lamp_1:/home/jiminny# ]DEV (d)-zshHomeDMsActivityLater...alo)• Preparation for Refi... in 58 m100% <78• Mon 11 May 14:02:31→Describe what you are looking forJiminny ...# contusion-clinic# curiosity_lab# engineering# general# jiminny-bg# platform-tickets# product_launches# random# releases# sofia-office# support# thank-yous# the_people_of jimi...ToastHomeMessages^ Direct messages®. Aneliya Angelova®. Galya Dimitrova I& Petko Kashinski&. Stefka StoyanovaVasil Vasilev&. Nikolay IvanovAneliya Angelova, ...Stoyan Tanev®. VesE Lukas Kovalik y... 0AboutiminnyFriday, May 8th ~Added by Toast for GitHubToday ~Toast APP10:00 AMReviewapp#12059 Jy 20820 es reindexstream model hydration2 days old • 12 files changed •@Vasil VasilevAdded by Toast for GitHubResolve Conflictsapp#11443 Test hublets latency5 months old • 20 files changed#11327 JY-19501 webhookbased opportunity syncShow moreAdded by Toast for GitHubNeeds Loveapp#12024 JY-20773 fix user pilottracking ofr automated reportgenerated12 days old • 1 file changedE:: AppsMessage ToastJira CloudToast+AaConala CalaNew...
|
18182
|
NULL
|
NULL
|
NULL
|
|
18185
|
789
|
22
|
2026-05-11T11:02:33.667345+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-11/1778 /Users/lukas/.screenpipe/data/data/2026-05-11/1778497353667_m2.jpg...
|
PhpStorm
|
faVsco.js – Client.php
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
JY-20725-handle-HS-search Project: faVsco.js, menu
JY-20725-handle-HS-search-rate-limit, menu
Start Listening for PHP Debug Connections
HandleHubspotRateLimitTest
Run 'HandleHubspotRateLimitTest'
Debug 'HandleHubspotRateLimitTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
2
67
3
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Services\Crm\Hubspot;
use HubSpot\Client\Crm\Deals\ApiException as DealApiException;
use HubSpot\Client\Crm\Contacts\ApiException as ContactApiException;
use HubSpot\Client\Crm\Companies\ApiException as CompanyApiException;
use HubSpot\Client\Crm\Contacts\Model\SimplePublicObjectWithAssociations as ContactsWithAssociations;
use HubSpot\Client\Crm\Companies\Model\SimplePublicObjectWithAssociations as CompaniesWithAssociations;
use HubSpot\Client\Crm\Deals\Model\SimplePublicObjectWithAssociations as DealWithAssociations;
use HubSpot\Client\Crm\Objects\Model\SimplePublicObjectInput;
use HubSpot\Client\Crm\Objects\Model\SimplePublicObjectWithAssociations as ObjectWithAssociations;
use HubSpot\Client\Crm\Pipelines\Model\Error;
use HubSpot\Client\Crm\Pipelines\Model\PipelineStage;
use HubSpot\Client\Crm\Properties\Model\Property;
use HubSpot\Discovery\Discovery;
use Jiminny\Exceptions\CrmException;
use Jiminny\Exceptions\RateLimitException;
use Jiminny\Exceptions\SocialAccountTokenInvalidException;
use Jiminny\Jobs\Crm\NoteObject;
use Jiminny\Models\Crm\Field;
use Jiminny\Services\Crm\BaseClient;
use Jiminny\Services\Crm\Hubspot\DTO\Response\Owner;
use Jiminny\Services\SocialAccountService;
use SevenShores\Hubspot\Exceptions\BadRequest;
use SevenShores\Hubspot\Exceptions\HubspotException;
use SevenShores\Hubspot\Factory;
use SevenShores\Hubspot\Http\Response;
use Jiminny\Services\Crm\Hubspot\Pagination\HubspotPaginationService;
use Illuminate\Support\Facades\Redis;
use Throwable;
/**
* @phpstan-type CrmFieldOption array{id:string, label:string, value?:string}
*/
class Client extends BaseClient implements HubspotClientInterface
{
public const string MIN_API_VERSION = '2';
public const string BASE_URL = '[URL_WITH_CREDENTIALS] T
* @param callable(): T $apiCall
* @return T
*
* @throws RateLimitException
*/
private function executeRequest(callable $apiCall)
{
$cacheKey = $this->getRateLimitCacheKey();
$cachedRetryAfter = Redis::get($cacheKey);
if (is_string($cachedRetryAfter) && is_numeric($cachedRetryAfter)) {
throw new RateLimitException(
'Hubspot rate limit (cached circuit-breaker)',
(int) $cachedRetryAfter,
);
}
try {
return $apiCall();
} catch (Throwable $e) {
if ($this->isHubspotRateLimit($e)) {
$retryAfter = $this->parseRetryAfter($e);
Redis::setex($cacheKey, $retryAfter, (string) $retryAfter);
$this->log->warning('[Hubspot] Received 429 from API', [
'team_id' => $this->config->team_id,
'config_id' => $this->config->getId(),
'retry_after' => $retryAfter,
'message' => $this->parseMessage($e),
'reason' => $e->getMessage(),
]);
throw new RateLimitException('Hubspot returned 429', $retryAfter, $e);
}
throw $e;
}
}
private function getRateLimitCacheKey(): string
{
return sprintf('hubspot:ratelimit:portal:%d', $this->config->getId());
}
public function isHubspotRateLimit(Throwable $e): bool
{
if ($e instanceof BadRequest
|| $e instanceof DealApiException
|| $e instanceof ContactApiException
|| $e instanceof CompanyApiException
|| $e instanceof \GuzzleHttp\Exception\RequestException
) {
return (int) $e->getCode() === 429;
}
return false;
}
public function parseRetryAfter(Throwable $e): int
{
if (method_exists($e, 'getResponseHeaders')) {
$headers = $e->getResponseHeaders() ?: [];
$value = $headers['Retry-After'] ?? $headers['retry-after'] ?? null;
if (is_array($value)) {
$value = $value[0] ?? null;
}
if (is_numeric($value)) {
return (int) $value;
}
}
$message = strtolower($this->parseMessage($e));
if (str_contains($message, 'daily')) {
return 600;
}
if (str_contains($message, 'ten secondly')) {
return 10;
}
if (str_contains($message, 'secondly')) {
return 1;
}
$this->log->warning('[Hubspot] No retry-after header or known message, using default', [
'exception_class' => get_class($e),
'message' => $message,
]);
return 10;
}
public function parseMessage(Throwable $e): string
{
if (! method_exists($e, 'getResponseBody')) {
return '';
}
$body = $e->getResponseBody();
if (is_string($body)) {
$body = json_decode($body, true) ?? [];
}
if (! is_array($body)) {
return '';
}
return (string) ($body['message'] ?? '');
}
public function getMinimumApiVersion(): string
{
return self::MIN_API_VERSION;
}
public function getInstance(): Factory
{
return new Factory([
'key' => $this->accessToken,
'oauth2' => true,
'base_url' => $this->baseUrl,
]);
}
public function getNewInstance(): Discovery
{
return \HubSpot\Factory::createWithAccessToken($this->accessToken);
}
/**
* Secondly and daily limits for Hubspot API
*
* Product Tier: Free & Starter | Professional & Enterprise | API add-on (any tier)
* Burst: 100/10 seconds | 150/10 seconds | 200/10 seconds
* Daily: 250,000 | 500,000 | 1,000,000
*
* Official documentation states: The search endpoints are rate limited to five requests per second.
* Since with 5 RPS were still hitting secondly rate limits we lowered it to 4
*/
public function getPaginatedData(array $payload, string $type, int $offset = 0): array
{
$total = 0;
$lastId = null;
$rows = [];
foreach ($this->getPaginatedDataGenerator($payload, $type, $offset, $total, $lastId) as $row) {
$rows[] = $row;
}
return ['results' => $rows, 'total' => $total, 'last_record' => $lastId];
}
/**
* @throws HubspotException
* @throws SocialAccountTokenInvalidException
* @throws BadRequest
*/
public function getPaginatedDataGenerator(
array $payload,
string $type,
int $offset = 0,
int &$total = 0,
?string &$lastRecordId = null
): \Generator {
return $this->paginationService->getPaginatedDataGenerator(
$this,
$payload,
$type,
$offset,
$total,
$lastRecordId
);
}
/**
* Execute a search request against HubSpot CRM objects with rate limiting.
*
* @param string $objectType The object type ('deals', 'companies', 'contacts', 'calls')
* @param array<string, mixed> $payload The search payload with filters, sorts, properties, etc.
* @return array The search response with 'results', 'total', 'paging' keys
* @throws RateLimitException When rate limit is hit
* @throws HubspotException On API errors
*/
public function search(string $objectType, array $payload): array
{
$endpoint = self::BASE_URL . "/crm/v3/objects/{$objectType}/search";
return $this->executeRequest(function () use ($endpoint, $payload) {
$response = $this->getInstance()->getClient()->request('POST', $endpoint, ['json' => $payload]);
return $response->toArray();
});
}
/**
* @throws DealApiException
* @throws CrmException
*/
public function getOpportunityById(string $crmId, array $fields): array
{
try {
// $deal = $this->executeRequest(fn () => $this->getNewInstance()->crm()->deals()->basicApi()->getById(
$deal = $this->getNewInstance()->crm()->deals()->basicApi()->getById(
$crmId,
implode(',', $fields),
'companies,contacts'
);
} catch (DealApiException $e) {
$this->log->info('[Hubspot] Failed to fetch opportunity', [
'crm_id' => $crmId,
'reason' => $e->getMessage(),
]);
throw $e;
}
if (! $deal instanceof DealWithAssociations) {
throw new CrmException('Deal not found');
}
return [
'id' => $deal->getId(),
'properties' => $deal->getProperties(),
'associations' => $deal->getAssociations(),
];
}
/**
* Generic batch read method for HubSpot objects
*
* @param string $objectType The object type ('deals', 'companies', 'contacts')
* @param array<string> $crmIds Array of HubSpot object IDs (max 100)
* @param array<string> $fields Array of property names to fetch
*
* @return array<string, array> Array keyed by CRM ID with object data
*/
private function batchReadObjects(string $objectType, array $crmIds, array $fields): array
{
if (empty($crmIds)) {
return [];
}
$this->validateBatchSize($objectType, $crmIds);
$this->ensureValidToken();
try {
$batchConfig = $this->createBatchConfiguration($objectType);
$batchReadRequest = $this->prepareBatchRequest($batchConfig, $crmIds, $fields);
$response = $batchConfig['api']->read($batchReadRequest);
$this->validateApiResponse($response, $objectType);
$results = $this->processApiResults($response);
$this->logBatchResults($objectType, $crmIds, $results);
return $results;
} catch (\Throwable $e) {
$this->handleBatchError($e, $objectType, $crmIds);
}
}
private function validateBatchSize(string $objectType, array $crmIds): void
{
if (count($crmIds) > 100) {
throw new \InvalidArgumentException("Batch size cannot exceed 100 {$objectType}");
}
}
private function createBatchConfiguration(string $objectType): array
{
$configurations = [
'deals' => [
'batchReadRequest' => new \HubSpot\Client\Crm\Deals\Model\BatchReadInputSimplePublicObjectId(),
'inputClass' => \HubSpot\Client\Crm\Deals\Model\SimplePublicObjectId::class,
'api' => $this->getNewInstance()->crm()->deals()->batchApi(),
],
'companies' => [
'batchReadRequest' => new \HubSpot\Client\Crm\Companies\Model\BatchReadInputSimplePublicObjectId(),
'inputClass' => \HubSpot\Client\Crm\Companies\Model\SimplePublicObjectId::class,
'api' => $this->getNewInstance()->crm()->companies()->batchApi(),
],
'contacts' => [
'batchReadRequest' => new \HubSpot\Client\Crm\Contacts\Model\BatchReadInputSimplePublicObjectId(),
'inputClass' => \HubSpot\Client\Crm\Contacts\Model\SimplePublicObjectId::class,
'api' => $this->getNewInstance()->crm()->contacts()->batchApi(),
],
];
if (! isset($configurations[$objectType])) {
throw new \InvalidArgumentException("Unsupported object type: {$objectType}");
}
return $configurations[$objectType];
}
private function prepareBatchRequest(array $batchConfig, array $crmIds, array $fields): object
{
$batchReadRequest = $batchConfig['batchReadRequest'];
$inputClass = $batchConfig['inputClass'];
$inputs = array_map(function ($crmId) use ($inputClass) {
$input = new $inputClass();
$input->setId($crmId);
return $input;
}, $crmIds);
$batchReadRequest->setInputs($inputs);
$batchReadRequest->setProperties($fields);
return $batchReadRequest;
}
private function validateApiResponse($response, string $objectType): void
{
if (! $response) {
throw new CrmException("HubSpot API returned null response for {$objectType} batch read");
}
}
private function processApiResults($response): array
{
$results = [];
$responseResults = $response->getResults();
if ($responseResults) {
foreach ($responseResults as $object) {
if ($object && $object->getId()) {
$results[$object->getId()] = [
'id' => $object->getId(),
'properties' => $object->getProperties() ?: [],
];
}
}
}
return $results;
}
private function logBatchResults(string $objectType, array $crmIds, array $results): void
{
$this->log->info("[HubSpot] Batch fetched {$objectType}", [
'requested_count' => count($crmIds),
'returned_count' => count($results),
'crm_ids' => $crmIds,
]);
}
private function handleBatchError(\Throwable $e, string $objectType, array $crmIds): void
{
$errorMessage = $e->getMessage() ?: 'Unknown error';
$errorTrace = $e->getTraceAsString() ?: 'No trace available';
$this->log->error("[HubSpot] Failed to batch fetch {$objectType}", [
'crm_ids' => $crmIds,
'error' => $errorMessage,
'trace' => $errorTrace,
]);
throw new CrmException("Failed to batch fetch {$objectType}: " . $errorMessage);
}
/**
* Batch read multiple opportunities by their CRM IDs
*
* @param array<string> $crmIds Array of HubSpot deal IDs (max 100)
* @param array<string> $fields Array of property names to fetch
*
* @return array<string, array> Array keyed by CRM ID with opportunity data
*/
public function getOpportunitiesByIds(array $crmIds, array $fields): array
{
return $this->batchReadObjects('deals', $crmIds, $fields);
}
/**
* Batch read multiple companies by their CRM IDs
*
* @param array<string> $crmIds Array of HubSpot company IDs (max 100)
* @param array<string> $fields Array of property names to fetch
*
* @return array<string, array> Array keyed by CRM ID with company data
*/
public function getCompaniesByIds(array $crmIds, array $fields): array
{
return $this->batchReadObjects('companies', $crmIds, $fields);
}
/**
* Batch read multiple contacts by their CRM IDs
*
* @param array<string> $crmIds Array of HubSpot contact IDs (max 100)
* @param array<string> $fields Array of property names to fetch
*
* @return array<string, array> Array keyed by CRM ID with contact data
*/
public function getContactsByIds(array $crmIds, array $fields): array
{
return $this->batchReadObjects('contacts', $crmIds, $fields);
}
/**
* @throws CompanyApiException
* @throws CrmException
*/
public function getAccountById(string $crmId, array $fields): array
{
try {
$company = $this->getNewInstance()->crm()->companies()->basicApi()->getById(
$crmId,
implode(',', $fields),
);
} catch (CompanyApiException $e) {
$this->log->info('[Hubspot] Failed to fetch account', [
'crm_id' => $crmId,
'reason' => $e->getMessage(),
]);
throw $e;
}
if (! $company instanceof CompaniesWithAssociations) {
throw new CrmException('Account not found');
}
return [
'id' => $company->getId(),
'properties' => $company->getProperties(),
];
}
/**
* @throws ContactApiException
* @throws CrmException
*/
public function getContactById(string $crmId, array $fields): array
{
try {
$contact = $this->getNewInstance()->crm()->contacts()->basicApi()->getById(
$crmId,
implode(',', $fields)
);
} catch (ContactApiException $e) {
$this->log->info('[Hubspot] Failed to fetch contact', [
'crm_id' => $crmId,
'reason' => $e->getMessage(),
]);
throw $e;
}
if (! $contact instanceof ContactsWithAssociations) {
throw new CrmException('Contact not found');
}
return [
'id' => $contact->getId(),
'properties' => $contact->getProperties(),
];
}
/**
* This is email search request that Hubspot offers as GET (more generous quota)
*/
public function getContactByEmail(string $email, array $fields = []): array
{
try {
$contact = $this->getNewInstance()->crm()->contacts()->basicApi()->getById(
$email,
implode(',', $fields),
null,
false,
'email'
);
return [
'id' => $contact->getId(),
'properties' => $contact->getProperties(),
];
} catch (ContactApiException $e) {
$this->log->info('[Hubspot] Failed to fetch contact', [
'email' => $email,
'reason' => $e->getMessage(),
]);
return [];
}
}
/**
* @throws CrmException
*/
public function fetchProperty(string $objectType, string $propertyId): Property
{
$result = $this->getNewInstance()->crm()->properties()->coreApi()->getByName($objectType, $propertyId);
if (! $result instanceof Property) {
$this->log->error('[Hubspot] Failed to fetch property', [
'object_type' => $objectType,
'property_id' => $propertyId,
'reason' => $result->getMessage(),
]);
throw new CrmException('Failed to fetch property');
}
return $result;
}
/**
* @return array<CrmFieldOption>
*/
public function fetchPropertyOptions(string $objectType, string $propertyId): array
{
/** @var array<CrmFieldOption> */
return $this->fetchProperty($objectType, $propertyId)->getOptions();
}
/**
* @return array<array{id:string, label:string, deleted:bool}>
*/
public function fetchCallDispositions(): array
{
/** @var Response $response */
$response = $this->getInstance()->engagements()->getCallDispositions();
/**
* @var array<array{
* id:string,
* label:string,
* deleted: bool
* }>
*/
return $response->toArray();
}
/**
* @return array<CrmFieldOption>
*/
public function fetchOpportunityPipelineStages(): array
{
$stages = [];
$apiResponse = $this->getNewInstance()->crm()->pipelines()->pipelinesApi()->getAll('deals');
if ($apiResponse instanceof Error) {
$this->log->error('[Hubspot] Failed to fetch opportunity pipelines', [
'reason' => $apiResponse->getMessage(),
]);
return [];
}
foreach ($apiResponse->getResults() as $pipeline) {
$pipelineStages = array_map(
static function (PipelineStage $stage) {
return [
'id' => $stage->getId(),
'label' => $stage->getLabel(),
];
},
$pipeline->getStages()
);
$stages = array_merge($stages, $pipelineStages);
}
return $stages;
}
public function fetchOpportunityPipelines(): array
{
$pipelines = [];
try {
$apiResponse = $this->makeRequest('/crm/v3/pipelines/deals');
} catch (\Exception $e) {
$this->log->info('[Hubspot] Failed to fetch opportunity pipelines', [
'reason' => $e->getMessage(),
]);
return [];
}
$response = $apiResponse->toArray();
foreach ($response['results'] as $pipeline) {
$pipelines[] = [
'id' => $pipeline['id'],
'label' => $pipeline['label'],
];
}
return $pipelines;
}
/**
* @return array<CrmFieldOption>
*/
public function fetchMeetingOutcomeFieldOptions(Field $field): array
{
return $field->getCrmProviderId() === 'meetingOutcome'
? $this->fetchMeetingOutcomeTypes()
: $this->fetchCallActivityTypes();
}
public function fetchMeetingOutcomeTypes(): array
{
return $this->extractMeetingTypeOptions(
'[URL_WITH_CREDENTIALS] Response $response */
$response = $this->getInstance()
->getClient()
->request('GET', $endpoint);
/**
* @var array<array{
* value: string,
* label: string,
* displayOrder: int
* }> $optionData
*/
$optionData = $response->toArray()['options'] ?? [];
$options = [];
foreach ($optionData as $item) {
$options[] = [
'id' => $item['value'],
'value' => $item['value'],
'label' => $item['label'],
'display_order' => $item['displayOrder'],
];
}
return $options;
}
/**
* @return array<CrmFieldOption>
*/
public function fetchDispositionFieldOptions(): array
{
$options = [];
$dispositions = $this->fetchCallDispositions();
foreach ($dispositions as $disposition) {
if ($disposition['deleted'] !== false) {
continue;
}
$option['value'] = $disposition['id'];
$option['id'] = $disposition['id'];
$option['label'] = $disposition['label'];
$options[] = $option;
}
return $options;
}
/**
* @return array<CrmFieldOption>
*/
public function fetchOpportunityFieldOptions(Field $field): array
{
if ($field->isStageField()) {
return $this->fetchOpportunityPipelineStages();
}
if ($field->isPipelineField()) {
return $this->fetchOpportunityPipelines();
}
return $this->fetchPropertyOptions('deals', $field->getCrmProviderId());
}
/**
* @throws BadRequest
* @throws HubspotException
*/
public function makeRequest(string $endpoint, $method = 'GET', $payload = [], ?string $queryString = null)
{
$endpoint = self::BASE_URL . $endpoint;
if ($method === 'GET') {
return $this->getInstance()->getClient()?->request(
method: $method,
endpoint: $endpoint,
query_string: $queryString
);
} else {
return $this->getInstance()->getClient()->request($method, $endpoint, [
'json' => ($payload),
]);
}
}
/**
* @throws BadRequest
* @throws HubspotException
*/
public function createMeeting(array $payload): Response
{
$endpoint = '/crm/v3/objects/meetings';
return $this->makeRequest($endpoint, 'POST', $payload);
}
/**
* @throws BadRequest
* @throws HubspotException
*/
public function updateMeeting(string $meetingId, array $payload): Response
{
$endpoint = '/crm/v3/objects/meetings/' . $meetingId;
return $this->makeRequest($endpoint, 'PATCH', $payload);
}
/**
* @throws \Exception
*/
public function createNote(
string $body,
string $ownerId,
int $timestamp,
string $objectId,
NoteObject $noteObject
): ?string {
try {
$noteInput = new SimplePublicObjectInput([
'properties' => [
'hs_note_body' => $body,
'hubspot_owner_id' => $ownerId,
'hs_timestamp' => $timestamp,
],
]);
// Create note
$note = $this->getNewInstance()->crm()->objects()->basicApi()->create('note', $noteInput);
$this->getNewInstance()->crm()->objects()->associationsApi()->create(
'note',
$note->getId(),
$this->getNoteObject($noteObject),
$objectId,
$this->getNoteAssociationType($noteObject),
);
return $note->getId();
} catch (\Exception $e) {
$this->log->error('[Hubspot] Failed to create note', [
'objectId' => $objectId,
'noteObject' => $noteObject->getObjectType(),
'reason' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
return null;
}
public function updateEngagement(string $objectId, array $engagement, array $metadata): void
{
$this->getInstance()->engagements()->update($objectId, $engagement, $metadata);
}
public function getEngagementData(string $engagementId): array
{
$engagement = $this->getInstance()->engagements()->get($engagementId);
return $engagement->toArray();
}
public function createEngagement(array $engagement, array $associations, array $metadata): Response
{
return $this->getInstance()
->engagements()
->create($engagement, $associations, $metadata);
}
public function isUnauthorizedException(\Exception $e): bool
{
// Check for specific HubSpot API exception types first
if ($e instanceof BadRequest) {
// BadRequest can contain 401 status codes
return $e->getCode() === 401;
}
// Check for HTTP client exceptions with status codes
if ($e instanceof \GuzzleHttp\Exception\RequestException && $e->hasResponse()) {
$response = $e->getResponse();
if ($response !== null) {
return $response->getStatusCode() === 401;
}
}
// Check for Guzzle HTTP exceptions
if ($e instanceof \GuzzleHttp\Exception\ClientException) {
return $e->getCode() === 401;
}
// Fallback to string matching as last resort, but be more specific
$message = strtolower($e->getMessage());
return str_contains($message, '401 unauthorized') ||
str_contains($message, 'http 401') ||
str_contains($message, 'status code 401') ||
(preg_match('/\b401\b/', $message) && str_contains($message, 'unauthorized'));
}
/**
* Validates and refreshes the access token if needed before API requests.
* This ensures long-running processes don't fail due to token expiration.
*
* @throws SocialAccountTokenInvalidException
*/
public function ensureValidToken(): void
{
if ($this->oauthAccount === null) {
return;
}
$newToken = $this->tokenManager->ensureValidToken($this->oauthAccount);
if ($newToken !== null) {
$this->accessToken = $newToken;
}
}
public function getConfig()
{
return $this->config;
}
// returns only active (archived=false)
public function getOwners(): array
{
return $this->getNewInstance()->crm()->owners()->getAll();
}
/**
* @param bool $archived
*
* @return array<Owner>|[]
*/
public function getOwnersArchived(bool $archived = true): array
{
$endpoint = '/crm/v3/owners';
$queryParams = [
'archived' => $archived ? 'true' : 'false',
];
$queryString = http_build_query($queryParams);
$owners = [];
try {
$response = $this->makeRequest(endpoint: $endpoint, queryString: $queryString);
$responseData = $response?->toArray();
foreach ($responseData['results'] as $result) {
try {
$owners[] = Owner::create($result);
} catch (Throwable $e) {
$this->log->error('[HubSpot] Failed to process owner data', [
'result' => $result,
'error' => $e->getMessage(),
]);
continue;
}
}
} catch (Throwable $e) {
$this->log->error('HubSpot] Failed to fetch owners', [
'archived' => $archived,
'error' => $e->getMessage(),
]);
return [];
}
return $owners;
}
public function getMeeting(string $engagementId): ObjectWithAssociations
{
return $this->getNewInstance()->crm()->objects()->basicApi()
->getById('meeting', $engagementId, null, 'contact,company,deal');
}
public function deleteEngagement(string $engagementId): void
{
$this->getInstance()->engagements()->delete((int) $engagementId);
}
public function getAssociationsData(array $ids, string $fromObject, string $toObject): array
{
$associationData = [];
$idChunks = array_chunk($ids, self::ASSOCIATIONS_BATCH_SIZE_LIMIT);
foreach ($idChunks as $idChunk) {
try {
$batchInput = new \HubSpot\Client\Crm\Associations\Model\BatchInputPublicObjectId();
$batchInput->setInputs(array_map(function ($id) {
$publicObjectId = new \HubSpot\Client\Crm\Associations\Model\PublicObjectId();
$publicObjectId->setId($id);
return $publicObjectId;
}, $idChunk));
$associatedObjectsData = $this
->getNewInstance()
->crm()
->associations()
->batchApi()
->read($fromObject, $toObject, $batchInput);
if ($associatedObjectsData instanceof \HubSpot\Client\Crm\Associations\Model\BatchResponsePublicAssociationMulti) {
foreach ($associatedObjectsData->getResults() as $association) {
$from = $association->getFrom()->getId();
$toAssociations = $association->getTo();
if (! empty($toAssociations)) {
$associationData[$from] = array_map(function ($item) {
return $item->getId();
}, $toAssociations);
}
}
}
// } catch (RateLimitException $e) {
// throw $e;
} catch (\Exception $e) {
$this->log->error('[Hubspot] Failed to fetch associations', [
'from_object' => $fromObject,
'to_object' => $toObject,
'reason' => $e->getMessage(),
]);
}
}
return $associationData;
}
/**
* @throws \Exception
*/
private function getNoteAssociationType(NoteObject $noteObject): string
{
return match($noteObject) {
NoteObject::Opportunity => 'note_to_deal',
NoteObject::Lead, NoteObject::Contact => 'note_to_contact', // or 'note_to_lead' if your portal supports it
NoteObject::Account => 'note_to_company',
NoteObject::Call, NoteObject::Event => throw new \Exception('Not supported'),
};
}
/**
* @throws \Exception
*/
private function getNoteObject(NoteObject $noteObject): string
{
return match($noteObject) {
NoteObject::Opportunity => 'deal',
NoteObject::Lead, NoteObject::Contact => 'contact',
NoteObject::Account => 'company',
NoteObject::Call, NoteObject::Event => throw new \Exception('Not supported'),
};
}
public function addAssociations(string $objectType, string $associationType, array $payload): Response
{
$endpoint = "/crm/v4/associations/$objectType/$associationType/batch/create";
return $this->makeRequest($endpoint, 'POST', $payload);
}
public function removeAssociations(string $objectType, string $associationType, array $payload): Response
{
$endpoint = "/crm/v4/associations/$objectType/$associationType/batch/archive";
return $this->makeRequest($endpoint, 'POST', $payload);
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
19
Previous Highlighted Error
Next Highlighted Error
[2026-05-07 14:21:15] local.INFO: [Hubspot] DEBUG Getting headers {
"headers":{
"Date":["Thu,07 May 2026 14:21:15 GMT"],
"Content-Type":["application/json;charset=utf-8"],
"Transfer-Encoding":["chunked"],
"Connection":["keep-alive"],
"CF-Ray":["9f80deb8db60dc3a-SOF"],
"CF-Cache-Status":["DYNAMIC"],
"Strict-Transport-Security":["max-age=31536000; includeSubDomains; preload"],
"Vary":["origin,
accept-encoding"],
"access-control-allow-credentials":["false"],
"server-timing":["hcid;desc=\"019e02d0-6fd8-7812-bdba-885b7ccb3ee3\",
cfr;desc=\"9f80deb8e7c6dc3a-IAD\""],
"x-content-type-options":["nosniff"],
"x-hubspot-correlation-id":["019e02d0-6fd8-7812-bdba-885b7ccb3ee3"],
"Set-Cookie":["__cf_bm=SIUrtdQgXVrik50pdqF6hZVYKhzTnQBidvMabeCtm0Y-1778163675-[IP_ADDRESS]-rI.ZggtDKxTge5zr8_2gbBfWMQQ.ufZEXDZyHz2mBUFdzdo2gTHEsOkXMSEShjK0hGYxNhUGM1ZoBpX7BcFZcHEjA7Cs_.SMUhUnd2nYjko; path=/; expires=Thu,
07-May-26 14:51:15 GMT; domain=.hubapi.com; HttpOnly; Secure; SameSite=None"],
"Report-To":["{
\"endpoints\":[{
\"url\":\"https:\\/\\/a.nel.cloudflare.com\\/report\\/v4?s=NYAlsVTP0fYm32qrSDjxYE4sd2RWRqiSp3wHsmdEgZlzoYdxI%2BIxVpHmsKn3O%2BKVA3mFIJ2m7YRECDGSM%2BW2IYTzo6FM4%2BdUIjURO8srzKSvJgZ%2BQ6R79arKQw3uHLlX\"}],
\"group\":\"cf-nel\",
\"max_age\":604800}"],
"NEL":["{
\"success_fraction\":0.01,
\"report_to\":\"cf-nel\",
\"max_age\":604800}"],
"Server":["cloudflare"]}} {
"correlation_id":"95236535-ec98-4541-b92a-adfa73b69eab",
"trace_id":"c7ab8365-903f-46d4-9403-0e5b551e3545"}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"bounds":{"left":0.025930852,"top":0.019952115,"width":0.03856383,"height":0.025538707},"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JY-20725-handle-HS-search-rate-limit, menu","depth":5,"bounds":{"left":0.064494684,"top":0.019952115,"width":0.09541223,"height":0.025538707},"on_screen":true,"help_text":"Git Branch: JY-20725-handle-HS-search-rate-limit","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"bounds":{"left":0.82413566,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"HandleHubspotRateLimitTest","depth":6,"bounds":{"left":0.8394282,"top":0.019952115,"width":0.076130316,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'HandleHubspotRateLimitTest'","depth":6,"bounds":{"left":0.9155585,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'HandleHubspotRateLimitTest'","depth":6,"bounds":{"left":0.9268617,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"bounds":{"left":0.9381649,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"bounds":{"left":0.96609044,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"bounds":{"left":0.9773936,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"bounds":{"left":0.9886968,"top":0.019952115,"width":0.011303186,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"2","depth":4,"bounds":{"left":0.41156915,"top":0.19952115,"width":0.007978723,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"67","depth":4,"bounds":{"left":0.42154256,"top":0.19952115,"width":0.009973404,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"3","depth":4,"bounds":{"left":0.43351063,"top":0.19952115,"width":0.007978723,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.4431516,"top":0.19792499,"width":0.00731383,"height":0.018355945},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.4504654,"top":0.19792499,"width":0.006981383,"height":0.018355945},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Services\\Crm\\Hubspot;\n\nuse HubSpot\\Client\\Crm\\Deals\\ApiException as DealApiException;\nuse HubSpot\\Client\\Crm\\Contacts\\ApiException as ContactApiException;\nuse HubSpot\\Client\\Crm\\Companies\\ApiException as CompanyApiException;\nuse HubSpot\\Client\\Crm\\Contacts\\Model\\SimplePublicObjectWithAssociations as ContactsWithAssociations;\nuse HubSpot\\Client\\Crm\\Companies\\Model\\SimplePublicObjectWithAssociations as CompaniesWithAssociations;\nuse HubSpot\\Client\\Crm\\Deals\\Model\\SimplePublicObjectWithAssociations as DealWithAssociations;\nuse HubSpot\\Client\\Crm\\Objects\\Model\\SimplePublicObjectInput;\nuse HubSpot\\Client\\Crm\\Objects\\Model\\SimplePublicObjectWithAssociations as ObjectWithAssociations;\nuse HubSpot\\Client\\Crm\\Pipelines\\Model\\Error;\nuse HubSpot\\Client\\Crm\\Pipelines\\Model\\PipelineStage;\nuse HubSpot\\Client\\Crm\\Properties\\Model\\Property;\nuse HubSpot\\Discovery\\Discovery;\nuse Jiminny\\Exceptions\\CrmException;\nuse Jiminny\\Exceptions\\RateLimitException;\nuse Jiminny\\Exceptions\\SocialAccountTokenInvalidException;\nuse Jiminny\\Jobs\\Crm\\NoteObject;\nuse Jiminny\\Models\\Crm\\Field;\nuse Jiminny\\Services\\Crm\\BaseClient;\nuse Jiminny\\Services\\Crm\\Hubspot\\DTO\\Response\\Owner;\nuse Jiminny\\Services\\SocialAccountService;\nuse SevenShores\\Hubspot\\Exceptions\\BadRequest;\nuse SevenShores\\Hubspot\\Exceptions\\HubspotException;\nuse SevenShores\\Hubspot\\Factory;\nuse SevenShores\\Hubspot\\Http\\Response;\nuse Jiminny\\Services\\Crm\\Hubspot\\Pagination\\HubspotPaginationService;\nuse Illuminate\\Support\\Facades\\Redis;\nuse Throwable;\n\n/**\n * @phpstan-type CrmFieldOption array{id:string, label:string, value?:string}\n */\nclass Client extends BaseClient implements HubspotClientInterface\n{\n public const string MIN_API_VERSION = '2';\n\n public const string BASE_URL = 'https://api.hubapi.com';\n\n public const int ASSOCIATIONS_BATCH_SIZE_LIMIT = 1000;\n\n private HubspotPaginationService $paginationService;\n private HubspotTokenManager $tokenManager;\n\n public function __construct(\n SocialAccountService $socialAccountService,\n HubspotPaginationService $paginationService,\n HubspotTokenManager $tokenManager\n ) {\n parent::__construct($socialAccountService);\n $this->paginationService = $paginationService;\n $this->tokenManager = $tokenManager;\n\n $this->setBaseUrl(self::BASE_URL);\n $this->setVersion(self::MIN_API_VERSION);\n }\n\n /**\n * Reacts to a rate limits (429) from HubSpot by translating it\n * into a RateLimitException carrying retry_after.\n *\n * Wrap any outbound HubSpot call (SDK or raw HTTP) like:\n *\n * $this->executeRequest(fn () => $this->getNewInstance()->crm()->...);\n *\n * @template T\n * @param callable(): T $apiCall\n * @return T\n *\n * @throws RateLimitException\n */\n private function executeRequest(callable $apiCall)\n {\n $cacheKey = $this->getRateLimitCacheKey();\n\n $cachedRetryAfter = Redis::get($cacheKey);\n if (is_string($cachedRetryAfter) && is_numeric($cachedRetryAfter)) {\n throw new RateLimitException(\n 'Hubspot rate limit (cached circuit-breaker)',\n (int) $cachedRetryAfter,\n );\n }\n\n try {\n return $apiCall();\n } catch (Throwable $e) {\n if ($this->isHubspotRateLimit($e)) {\n $retryAfter = $this->parseRetryAfter($e);\n\n Redis::setex($cacheKey, $retryAfter, (string) $retryAfter);\n\n $this->log->warning('[Hubspot] Received 429 from API', [\n 'team_id' => $this->config->team_id,\n 'config_id' => $this->config->getId(),\n 'retry_after' => $retryAfter,\n 'message' => $this->parseMessage($e),\n 'reason' => $e->getMessage(),\n ]);\n\n throw new RateLimitException('Hubspot returned 429', $retryAfter, $e);\n }\n\n throw $e;\n }\n }\n\n private function getRateLimitCacheKey(): string\n {\n return sprintf('hubspot:ratelimit:portal:%d', $this->config->getId());\n }\n\n public function isHubspotRateLimit(Throwable $e): bool\n {\n if ($e instanceof BadRequest\n || $e instanceof DealApiException\n || $e instanceof ContactApiException\n || $e instanceof CompanyApiException\n || $e instanceof \\GuzzleHttp\\Exception\\RequestException\n ) {\n return (int) $e->getCode() === 429;\n }\n\n return false;\n }\n\n public function parseRetryAfter(Throwable $e): int\n {\n if (method_exists($e, 'getResponseHeaders')) {\n $headers = $e->getResponseHeaders() ?: [];\n $value = $headers['Retry-After'] ?? $headers['retry-after'] ?? null;\n if (is_array($value)) {\n $value = $value[0] ?? null;\n }\n if (is_numeric($value)) {\n return (int) $value;\n }\n }\n\n $message = strtolower($this->parseMessage($e));\n\n if (str_contains($message, 'daily')) {\n return 600;\n }\n if (str_contains($message, 'ten secondly')) {\n return 10;\n }\n if (str_contains($message, 'secondly')) {\n return 1;\n }\n\n $this->log->warning('[Hubspot] No retry-after header or known message, using default', [\n 'exception_class' => get_class($e),\n 'message' => $message,\n ]);\n\n return 10;\n }\n\n public function parseMessage(Throwable $e): string\n {\n if (! method_exists($e, 'getResponseBody')) {\n return '';\n }\n\n $body = $e->getResponseBody();\n if (is_string($body)) {\n $body = json_decode($body, true) ?? [];\n }\n\n if (! is_array($body)) {\n return '';\n }\n\n return (string) ($body['message'] ?? '');\n }\n\n public function getMinimumApiVersion(): string\n {\n return self::MIN_API_VERSION;\n }\n\n public function getInstance(): Factory\n {\n return new Factory([\n 'key' => $this->accessToken,\n 'oauth2' => true,\n 'base_url' => $this->baseUrl,\n ]);\n }\n\n public function getNewInstance(): Discovery\n {\n return \\HubSpot\\Factory::createWithAccessToken($this->accessToken);\n }\n\n /**\n * Secondly and daily limits for Hubspot API\n *\n * Product Tier: Free & Starter | Professional & Enterprise | API add-on (any tier)\n * Burst: 100/10 seconds | 150/10 seconds | 200/10 seconds\n * Daily: 250,000 | 500,000 | 1,000,000\n *\n * Official documentation states: The search endpoints are rate limited to five requests per second.\n * Since with 5 RPS were still hitting secondly rate limits we lowered it to 4\n */\n public function getPaginatedData(array $payload, string $type, int $offset = 0): array\n {\n $total = 0;\n $lastId = null;\n $rows = [];\n foreach ($this->getPaginatedDataGenerator($payload, $type, $offset, $total, $lastId) as $row) {\n $rows[] = $row;\n }\n\n return ['results' => $rows, 'total' => $total, 'last_record' => $lastId];\n }\n\n /**\n * @throws HubspotException\n * @throws SocialAccountTokenInvalidException\n * @throws BadRequest\n */\n public function getPaginatedDataGenerator(\n array $payload,\n string $type,\n int $offset = 0,\n int &$total = 0,\n ?string &$lastRecordId = null\n ): \\Generator {\n return $this->paginationService->getPaginatedDataGenerator(\n $this,\n $payload,\n $type,\n $offset,\n $total,\n $lastRecordId\n );\n }\n\n /**\n * Execute a search request against HubSpot CRM objects with rate limiting.\n *\n * @param string $objectType The object type ('deals', 'companies', 'contacts', 'calls')\n * @param array<string, mixed> $payload The search payload with filters, sorts, properties, etc.\n * @return array The search response with 'results', 'total', 'paging' keys\n * @throws RateLimitException When rate limit is hit\n * @throws HubspotException On API errors\n */\n public function search(string $objectType, array $payload): array\n {\n $endpoint = self::BASE_URL . \"/crm/v3/objects/{$objectType}/search\";\n\n return $this->executeRequest(function () use ($endpoint, $payload) {\n $response = $this->getInstance()->getClient()->request('POST', $endpoint, ['json' => $payload]);\n\n return $response->toArray();\n });\n }\n\n /**\n * @throws DealApiException\n * @throws CrmException\n */\n public function getOpportunityById(string $crmId, array $fields): array\n {\n try {\n// $deal = $this->executeRequest(fn () => $this->getNewInstance()->crm()->deals()->basicApi()->getById(\n $deal = $this->getNewInstance()->crm()->deals()->basicApi()->getById(\n $crmId,\n implode(',', $fields),\n 'companies,contacts'\n );\n } catch (DealApiException $e) {\n $this->log->info('[Hubspot] Failed to fetch opportunity', [\n 'crm_id' => $crmId,\n 'reason' => $e->getMessage(),\n ]);\n\n throw $e;\n }\n\n if (! $deal instanceof DealWithAssociations) {\n throw new CrmException('Deal not found');\n }\n\n return [\n 'id' => $deal->getId(),\n 'properties' => $deal->getProperties(),\n 'associations' => $deal->getAssociations(),\n ];\n }\n\n /**\n * Generic batch read method for HubSpot objects\n *\n * @param string $objectType The object type ('deals', 'companies', 'contacts')\n * @param array<string> $crmIds Array of HubSpot object IDs (max 100)\n * @param array<string> $fields Array of property names to fetch\n *\n * @return array<string, array> Array keyed by CRM ID with object data\n */\n private function batchReadObjects(string $objectType, array $crmIds, array $fields): array\n {\n if (empty($crmIds)) {\n return [];\n }\n\n $this->validateBatchSize($objectType, $crmIds);\n $this->ensureValidToken();\n\n try {\n $batchConfig = $this->createBatchConfiguration($objectType);\n $batchReadRequest = $this->prepareBatchRequest($batchConfig, $crmIds, $fields);\n $response = $batchConfig['api']->read($batchReadRequest);\n\n $this->validateApiResponse($response, $objectType);\n\n $results = $this->processApiResults($response);\n $this->logBatchResults($objectType, $crmIds, $results);\n\n return $results;\n } catch (\\Throwable $e) {\n $this->handleBatchError($e, $objectType, $crmIds);\n }\n }\n\n private function validateBatchSize(string $objectType, array $crmIds): void\n {\n if (count($crmIds) > 100) {\n throw new \\InvalidArgumentException(\"Batch size cannot exceed 100 {$objectType}\");\n }\n }\n\n private function createBatchConfiguration(string $objectType): array\n {\n $configurations = [\n 'deals' => [\n 'batchReadRequest' => new \\HubSpot\\Client\\Crm\\Deals\\Model\\BatchReadInputSimplePublicObjectId(),\n 'inputClass' => \\HubSpot\\Client\\Crm\\Deals\\Model\\SimplePublicObjectId::class,\n 'api' => $this->getNewInstance()->crm()->deals()->batchApi(),\n ],\n 'companies' => [\n 'batchReadRequest' => new \\HubSpot\\Client\\Crm\\Companies\\Model\\BatchReadInputSimplePublicObjectId(),\n 'inputClass' => \\HubSpot\\Client\\Crm\\Companies\\Model\\SimplePublicObjectId::class,\n 'api' => $this->getNewInstance()->crm()->companies()->batchApi(),\n ],\n 'contacts' => [\n 'batchReadRequest' => new \\HubSpot\\Client\\Crm\\Contacts\\Model\\BatchReadInputSimplePublicObjectId(),\n 'inputClass' => \\HubSpot\\Client\\Crm\\Contacts\\Model\\SimplePublicObjectId::class,\n 'api' => $this->getNewInstance()->crm()->contacts()->batchApi(),\n ],\n ];\n\n if (! isset($configurations[$objectType])) {\n throw new \\InvalidArgumentException(\"Unsupported object type: {$objectType}\");\n }\n\n return $configurations[$objectType];\n }\n\n private function prepareBatchRequest(array $batchConfig, array $crmIds, array $fields): object\n {\n $batchReadRequest = $batchConfig['batchReadRequest'];\n $inputClass = $batchConfig['inputClass'];\n\n $inputs = array_map(function ($crmId) use ($inputClass) {\n $input = new $inputClass();\n $input->setId($crmId);\n\n return $input;\n }, $crmIds);\n\n $batchReadRequest->setInputs($inputs);\n $batchReadRequest->setProperties($fields);\n\n return $batchReadRequest;\n }\n\n private function validateApiResponse($response, string $objectType): void\n {\n if (! $response) {\n throw new CrmException(\"HubSpot API returned null response for {$objectType} batch read\");\n }\n }\n\n private function processApiResults($response): array\n {\n $results = [];\n $responseResults = $response->getResults();\n\n if ($responseResults) {\n foreach ($responseResults as $object) {\n if ($object && $object->getId()) {\n $results[$object->getId()] = [\n 'id' => $object->getId(),\n 'properties' => $object->getProperties() ?: [],\n ];\n }\n }\n }\n\n return $results;\n }\n\n private function logBatchResults(string $objectType, array $crmIds, array $results): void\n {\n $this->log->info(\"[HubSpot] Batch fetched {$objectType}\", [\n 'requested_count' => count($crmIds),\n 'returned_count' => count($results),\n 'crm_ids' => $crmIds,\n ]);\n }\n\n private function handleBatchError(\\Throwable $e, string $objectType, array $crmIds): void\n {\n $errorMessage = $e->getMessage() ?: 'Unknown error';\n $errorTrace = $e->getTraceAsString() ?: 'No trace available';\n\n $this->log->error(\"[HubSpot] Failed to batch fetch {$objectType}\", [\n 'crm_ids' => $crmIds,\n 'error' => $errorMessage,\n 'trace' => $errorTrace,\n ]);\n\n throw new CrmException(\"Failed to batch fetch {$objectType}: \" . $errorMessage);\n }\n\n /**\n * Batch read multiple opportunities by their CRM IDs\n *\n * @param array<string> $crmIds Array of HubSpot deal IDs (max 100)\n * @param array<string> $fields Array of property names to fetch\n *\n * @return array<string, array> Array keyed by CRM ID with opportunity data\n */\n public function getOpportunitiesByIds(array $crmIds, array $fields): array\n {\n return $this->batchReadObjects('deals', $crmIds, $fields);\n }\n\n /**\n * Batch read multiple companies by their CRM IDs\n *\n * @param array<string> $crmIds Array of HubSpot company IDs (max 100)\n * @param array<string> $fields Array of property names to fetch\n *\n * @return array<string, array> Array keyed by CRM ID with company data\n */\n public function getCompaniesByIds(array $crmIds, array $fields): array\n {\n return $this->batchReadObjects('companies', $crmIds, $fields);\n }\n\n /**\n * Batch read multiple contacts by their CRM IDs\n *\n * @param array<string> $crmIds Array of HubSpot contact IDs (max 100)\n * @param array<string> $fields Array of property names to fetch\n *\n * @return array<string, array> Array keyed by CRM ID with contact data\n */\n public function getContactsByIds(array $crmIds, array $fields): array\n {\n return $this->batchReadObjects('contacts', $crmIds, $fields);\n }\n\n /**\n * @throws CompanyApiException\n * @throws CrmException\n */\n public function getAccountById(string $crmId, array $fields): array\n {\n try {\n $company = $this->getNewInstance()->crm()->companies()->basicApi()->getById(\n $crmId,\n implode(',', $fields),\n );\n } catch (CompanyApiException $e) {\n $this->log->info('[Hubspot] Failed to fetch account', [\n 'crm_id' => $crmId,\n 'reason' => $e->getMessage(),\n ]);\n\n throw $e;\n }\n\n if (! $company instanceof CompaniesWithAssociations) {\n throw new CrmException('Account not found');\n }\n\n return [\n 'id' => $company->getId(),\n 'properties' => $company->getProperties(),\n ];\n }\n\n /**\n * @throws ContactApiException\n * @throws CrmException\n */\n public function getContactById(string $crmId, array $fields): array\n {\n try {\n $contact = $this->getNewInstance()->crm()->contacts()->basicApi()->getById(\n $crmId,\n implode(',', $fields)\n );\n } catch (ContactApiException $e) {\n $this->log->info('[Hubspot] Failed to fetch contact', [\n 'crm_id' => $crmId,\n 'reason' => $e->getMessage(),\n ]);\n\n throw $e;\n }\n\n if (! $contact instanceof ContactsWithAssociations) {\n throw new CrmException('Contact not found');\n }\n\n return [\n 'id' => $contact->getId(),\n 'properties' => $contact->getProperties(),\n ];\n }\n\n /**\n * This is email search request that Hubspot offers as GET (more generous quota)\n */\n public function getContactByEmail(string $email, array $fields = []): array\n {\n try {\n $contact = $this->getNewInstance()->crm()->contacts()->basicApi()->getById(\n $email,\n implode(',', $fields),\n null,\n false,\n 'email'\n );\n\n return [\n 'id' => $contact->getId(),\n 'properties' => $contact->getProperties(),\n ];\n } catch (ContactApiException $e) {\n $this->log->info('[Hubspot] Failed to fetch contact', [\n 'email' => $email,\n 'reason' => $e->getMessage(),\n ]);\n\n return [];\n }\n }\n\n /**\n * @throws CrmException\n */\n public function fetchProperty(string $objectType, string $propertyId): Property\n {\n $result = $this->getNewInstance()->crm()->properties()->coreApi()->getByName($objectType, $propertyId);\n\n if (! $result instanceof Property) {\n $this->log->error('[Hubspot] Failed to fetch property', [\n 'object_type' => $objectType,\n 'property_id' => $propertyId,\n 'reason' => $result->getMessage(),\n ]);\n\n throw new CrmException('Failed to fetch property');\n }\n\n return $result;\n }\n\n /**\n * @return array<CrmFieldOption>\n */\n public function fetchPropertyOptions(string $objectType, string $propertyId): array\n {\n /** @var array<CrmFieldOption> */\n return $this->fetchProperty($objectType, $propertyId)->getOptions();\n }\n\n /**\n * @return array<array{id:string, label:string, deleted:bool}>\n */\n public function fetchCallDispositions(): array\n {\n /** @var Response $response */\n $response = $this->getInstance()->engagements()->getCallDispositions();\n\n /**\n * @var array<array{\n * id:string,\n * label:string,\n * deleted: bool\n * }>\n */\n return $response->toArray();\n }\n\n /**\n * @return array<CrmFieldOption>\n */\n public function fetchOpportunityPipelineStages(): array\n {\n $stages = [];\n $apiResponse = $this->getNewInstance()->crm()->pipelines()->pipelinesApi()->getAll('deals');\n\n if ($apiResponse instanceof Error) {\n $this->log->error('[Hubspot] Failed to fetch opportunity pipelines', [\n 'reason' => $apiResponse->getMessage(),\n ]);\n\n return [];\n }\n\n foreach ($apiResponse->getResults() as $pipeline) {\n $pipelineStages = array_map(\n static function (PipelineStage $stage) {\n return [\n 'id' => $stage->getId(),\n 'label' => $stage->getLabel(),\n ];\n },\n $pipeline->getStages()\n );\n\n $stages = array_merge($stages, $pipelineStages);\n }\n\n return $stages;\n }\n\n public function fetchOpportunityPipelines(): array\n {\n $pipelines = [];\n\n try {\n $apiResponse = $this->makeRequest('/crm/v3/pipelines/deals');\n } catch (\\Exception $e) {\n $this->log->info('[Hubspot] Failed to fetch opportunity pipelines', [\n 'reason' => $e->getMessage(),\n ]);\n\n return [];\n }\n\n $response = $apiResponse->toArray();\n\n foreach ($response['results'] as $pipeline) {\n $pipelines[] = [\n 'id' => $pipeline['id'],\n 'label' => $pipeline['label'],\n ];\n }\n\n return $pipelines;\n }\n\n /**\n * @return array<CrmFieldOption>\n */\n public function fetchMeetingOutcomeFieldOptions(Field $field): array\n {\n return $field->getCrmProviderId() === 'meetingOutcome'\n ? $this->fetchMeetingOutcomeTypes()\n : $this->fetchCallActivityTypes();\n }\n\n public function fetchMeetingOutcomeTypes(): array\n {\n return $this->extractMeetingTypeOptions(\n 'https://api.hubapi.com/crm/v3/properties/meeting/hs_meeting_outcome'\n );\n }\n\n public function fetchCallActivityTypes(): array\n {\n return $this->extractMeetingTypeOptions(\n 'https://api.hubapi.com/crm/v3/properties/call/hs_activity_type'\n );\n }\n\n private function extractMeetingTypeOptions(string $endpoint): array\n {\n /** @var Response $response */\n $response = $this->getInstance()\n ->getClient()\n ->request('GET', $endpoint);\n\n /**\n * @var array<array{\n * value: string,\n * label: string,\n * displayOrder: int\n * }> $optionData\n */\n $optionData = $response->toArray()['options'] ?? [];\n\n $options = [];\n foreach ($optionData as $item) {\n $options[] = [\n 'id' => $item['value'],\n 'value' => $item['value'],\n 'label' => $item['label'],\n 'display_order' => $item['displayOrder'],\n ];\n }\n\n return $options;\n }\n\n /**\n * @return array<CrmFieldOption>\n */\n public function fetchDispositionFieldOptions(): array\n {\n $options = [];\n\n $dispositions = $this->fetchCallDispositions();\n\n foreach ($dispositions as $disposition) {\n if ($disposition['deleted'] !== false) {\n continue;\n }\n\n $option['value'] = $disposition['id'];\n $option['id'] = $disposition['id'];\n $option['label'] = $disposition['label'];\n\n $options[] = $option;\n }\n\n return $options;\n }\n\n /**\n * @return array<CrmFieldOption>\n */\n public function fetchOpportunityFieldOptions(Field $field): array\n {\n if ($field->isStageField()) {\n return $this->fetchOpportunityPipelineStages();\n }\n\n if ($field->isPipelineField()) {\n return $this->fetchOpportunityPipelines();\n }\n\n return $this->fetchPropertyOptions('deals', $field->getCrmProviderId());\n }\n\n /**\n * @throws BadRequest\n * @throws HubspotException\n */\n public function makeRequest(string $endpoint, $method = 'GET', $payload = [], ?string $queryString = null)\n {\n $endpoint = self::BASE_URL . $endpoint;\n\n if ($method === 'GET') {\n return $this->getInstance()->getClient()?->request(\n method: $method,\n endpoint: $endpoint,\n query_string: $queryString\n );\n } else {\n return $this->getInstance()->getClient()->request($method, $endpoint, [\n 'json' => ($payload),\n ]);\n }\n }\n\n /**\n * @throws BadRequest\n * @throws HubspotException\n */\n public function createMeeting(array $payload): Response\n {\n $endpoint = '/crm/v3/objects/meetings';\n\n return $this->makeRequest($endpoint, 'POST', $payload);\n }\n\n /**\n * @throws BadRequest\n * @throws HubspotException\n */\n public function updateMeeting(string $meetingId, array $payload): Response\n {\n $endpoint = '/crm/v3/objects/meetings/' . $meetingId;\n\n return $this->makeRequest($endpoint, 'PATCH', $payload);\n }\n\n /**\n * @throws \\Exception\n */\n public function createNote(\n string $body,\n string $ownerId,\n int $timestamp,\n string $objectId,\n NoteObject $noteObject\n ): ?string {\n try {\n $noteInput = new SimplePublicObjectInput([\n 'properties' => [\n 'hs_note_body' => $body,\n 'hubspot_owner_id' => $ownerId,\n 'hs_timestamp' => $timestamp,\n ],\n ]);\n\n // Create note\n $note = $this->getNewInstance()->crm()->objects()->basicApi()->create('note', $noteInput);\n\n $this->getNewInstance()->crm()->objects()->associationsApi()->create(\n 'note',\n $note->getId(),\n $this->getNoteObject($noteObject),\n $objectId,\n $this->getNoteAssociationType($noteObject),\n );\n\n return $note->getId();\n } catch (\\Exception $e) {\n $this->log->error('[Hubspot] Failed to create note', [\n 'objectId' => $objectId,\n 'noteObject' => $noteObject->getObjectType(),\n 'reason' => $e->getMessage(),\n ]);\n\n \\Sentry::captureException($e);\n }\n\n return null;\n }\n\n public function updateEngagement(string $objectId, array $engagement, array $metadata): void\n {\n $this->getInstance()->engagements()->update($objectId, $engagement, $metadata);\n }\n\n public function getEngagementData(string $engagementId): array\n {\n $engagement = $this->getInstance()->engagements()->get($engagementId);\n\n return $engagement->toArray();\n }\n\n public function createEngagement(array $engagement, array $associations, array $metadata): Response\n {\n return $this->getInstance()\n ->engagements()\n ->create($engagement, $associations, $metadata);\n }\n\n public function isUnauthorizedException(\\Exception $e): bool\n {\n // Check for specific HubSpot API exception types first\n if ($e instanceof BadRequest) {\n // BadRequest can contain 401 status codes\n return $e->getCode() === 401;\n }\n\n // Check for HTTP client exceptions with status codes\n if ($e instanceof \\GuzzleHttp\\Exception\\RequestException && $e->hasResponse()) {\n $response = $e->getResponse();\n if ($response !== null) {\n return $response->getStatusCode() === 401;\n }\n }\n\n // Check for Guzzle HTTP exceptions\n if ($e instanceof \\GuzzleHttp\\Exception\\ClientException) {\n return $e->getCode() === 401;\n }\n\n // Fallback to string matching as last resort, but be more specific\n $message = strtolower($e->getMessage());\n\n return str_contains($message, '401 unauthorized') ||\n str_contains($message, 'http 401') ||\n str_contains($message, 'status code 401') ||\n (preg_match('/\\b401\\b/', $message) && str_contains($message, 'unauthorized'));\n }\n\n /**\n * Validates and refreshes the access token if needed before API requests.\n * This ensures long-running processes don't fail due to token expiration.\n *\n * @throws SocialAccountTokenInvalidException\n */\n public function ensureValidToken(): void\n {\n if ($this->oauthAccount === null) {\n return;\n }\n\n $newToken = $this->tokenManager->ensureValidToken($this->oauthAccount);\n if ($newToken !== null) {\n $this->accessToken = $newToken;\n }\n }\n\n public function getConfig()\n {\n return $this->config;\n }\n\n // returns only active (archived=false)\n public function getOwners(): array\n {\n return $this->getNewInstance()->crm()->owners()->getAll();\n }\n\n /**\n * @param bool $archived\n *\n * @return array<Owner>|[]\n */\n public function getOwnersArchived(bool $archived = true): array\n {\n $endpoint = '/crm/v3/owners';\n $queryParams = [\n 'archived' => $archived ? 'true' : 'false',\n ];\n $queryString = http_build_query($queryParams);\n\n $owners = [];\n\n try {\n $response = $this->makeRequest(endpoint: $endpoint, queryString: $queryString);\n $responseData = $response?->toArray();\n\n foreach ($responseData['results'] as $result) {\n try {\n $owners[] = Owner::create($result);\n } catch (Throwable $e) {\n $this->log->error('[HubSpot] Failed to process owner data', [\n 'result' => $result,\n 'error' => $e->getMessage(),\n ]);\n\n continue;\n }\n }\n } catch (Throwable $e) {\n $this->log->error('HubSpot] Failed to fetch owners', [\n 'archived' => $archived,\n 'error' => $e->getMessage(),\n ]);\n\n return [];\n }\n\n return $owners;\n }\n\n public function getMeeting(string $engagementId): ObjectWithAssociations\n {\n return $this->getNewInstance()->crm()->objects()->basicApi()\n ->getById('meeting', $engagementId, null, 'contact,company,deal');\n }\n\n public function deleteEngagement(string $engagementId): void\n {\n $this->getInstance()->engagements()->delete((int) $engagementId);\n }\n\n public function getAssociationsData(array $ids, string $fromObject, string $toObject): array\n {\n $associationData = [];\n $idChunks = array_chunk($ids, self::ASSOCIATIONS_BATCH_SIZE_LIMIT);\n\n foreach ($idChunks as $idChunk) {\n try {\n $batchInput = new \\HubSpot\\Client\\Crm\\Associations\\Model\\BatchInputPublicObjectId();\n $batchInput->setInputs(array_map(function ($id) {\n $publicObjectId = new \\HubSpot\\Client\\Crm\\Associations\\Model\\PublicObjectId();\n $publicObjectId->setId($id);\n\n return $publicObjectId;\n }, $idChunk));\n\n $associatedObjectsData = $this\n ->getNewInstance()\n ->crm()\n ->associations()\n ->batchApi()\n ->read($fromObject, $toObject, $batchInput);\n\n if ($associatedObjectsData instanceof \\HubSpot\\Client\\Crm\\Associations\\Model\\BatchResponsePublicAssociationMulti) {\n foreach ($associatedObjectsData->getResults() as $association) {\n $from = $association->getFrom()->getId();\n $toAssociations = $association->getTo();\n\n if (! empty($toAssociations)) {\n $associationData[$from] = array_map(function ($item) {\n return $item->getId();\n }, $toAssociations);\n }\n }\n }\n// } catch (RateLimitException $e) {\n// throw $e;\n } catch (\\Exception $e) {\n $this->log->error('[Hubspot] Failed to fetch associations', [\n 'from_object' => $fromObject,\n 'to_object' => $toObject,\n 'reason' => $e->getMessage(),\n ]);\n }\n }\n\n return $associationData;\n }\n\n /**\n * @throws \\Exception\n */\n private function getNoteAssociationType(NoteObject $noteObject): string\n {\n return match($noteObject) {\n NoteObject::Opportunity => 'note_to_deal',\n NoteObject::Lead, NoteObject::Contact => 'note_to_contact', // or 'note_to_lead' if your portal supports it\n NoteObject::Account => 'note_to_company',\n NoteObject::Call, NoteObject::Event => throw new \\Exception('Not supported'),\n };\n }\n\n /**\n * @throws \\Exception\n */\n private function getNoteObject(NoteObject $noteObject): string\n {\n return match($noteObject) {\n NoteObject::Opportunity => 'deal',\n NoteObject::Lead, NoteObject::Contact => 'contact',\n NoteObject::Account => 'company',\n NoteObject::Call, NoteObject::Event => throw new \\Exception('Not supported'),\n };\n }\n\n public function addAssociations(string $objectType, string $associationType, array $payload): Response\n {\n $endpoint = \"/crm/v4/associations/$objectType/$associationType/batch/create\";\n\n return $this->makeRequest($endpoint, 'POST', $payload);\n }\n\n public function removeAssociations(string $objectType, string $associationType, array $payload): Response\n {\n $endpoint = \"/crm/v4/associations/$objectType/$associationType/batch/archive\";\n\n return $this->makeRequest($endpoint, 'POST', $payload);\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Services\\Crm\\Hubspot;\n\nuse HubSpot\\Client\\Crm\\Deals\\ApiException as DealApiException;\nuse HubSpot\\Client\\Crm\\Contacts\\ApiException as ContactApiException;\nuse HubSpot\\Client\\Crm\\Companies\\ApiException as CompanyApiException;\nuse HubSpot\\Client\\Crm\\Contacts\\Model\\SimplePublicObjectWithAssociations as ContactsWithAssociations;\nuse HubSpot\\Client\\Crm\\Companies\\Model\\SimplePublicObjectWithAssociations as CompaniesWithAssociations;\nuse HubSpot\\Client\\Crm\\Deals\\Model\\SimplePublicObjectWithAssociations as DealWithAssociations;\nuse HubSpot\\Client\\Crm\\Objects\\Model\\SimplePublicObjectInput;\nuse HubSpot\\Client\\Crm\\Objects\\Model\\SimplePublicObjectWithAssociations as ObjectWithAssociations;\nuse HubSpot\\Client\\Crm\\Pipelines\\Model\\Error;\nuse HubSpot\\Client\\Crm\\Pipelines\\Model\\PipelineStage;\nuse HubSpot\\Client\\Crm\\Properties\\Model\\Property;\nuse HubSpot\\Discovery\\Discovery;\nuse Jiminny\\Exceptions\\CrmException;\nuse Jiminny\\Exceptions\\RateLimitException;\nuse Jiminny\\Exceptions\\SocialAccountTokenInvalidException;\nuse Jiminny\\Jobs\\Crm\\NoteObject;\nuse Jiminny\\Models\\Crm\\Field;\nuse Jiminny\\Services\\Crm\\BaseClient;\nuse Jiminny\\Services\\Crm\\Hubspot\\DTO\\Response\\Owner;\nuse Jiminny\\Services\\SocialAccountService;\nuse SevenShores\\Hubspot\\Exceptions\\BadRequest;\nuse SevenShores\\Hubspot\\Exceptions\\HubspotException;\nuse SevenShores\\Hubspot\\Factory;\nuse SevenShores\\Hubspot\\Http\\Response;\nuse Jiminny\\Services\\Crm\\Hubspot\\Pagination\\HubspotPaginationService;\nuse Illuminate\\Support\\Facades\\Redis;\nuse Throwable;\n\n/**\n * @phpstan-type CrmFieldOption array{id:string, label:string, value?:string}\n */\nclass Client extends BaseClient implements HubspotClientInterface\n{\n public const string MIN_API_VERSION = '2';\n\n public const string BASE_URL = 'https://api.hubapi.com';\n\n public const int ASSOCIATIONS_BATCH_SIZE_LIMIT = 1000;\n\n private HubspotPaginationService $paginationService;\n private HubspotTokenManager $tokenManager;\n\n public function __construct(\n SocialAccountService $socialAccountService,\n HubspotPaginationService $paginationService,\n HubspotTokenManager $tokenManager\n ) {\n parent::__construct($socialAccountService);\n $this->paginationService = $paginationService;\n $this->tokenManager = $tokenManager;\n\n $this->setBaseUrl(self::BASE_URL);\n $this->setVersion(self::MIN_API_VERSION);\n }\n\n /**\n * Reacts to a rate limits (429) from HubSpot by translating it\n * into a RateLimitException carrying retry_after.\n *\n * Wrap any outbound HubSpot call (SDK or raw HTTP) like:\n *\n * $this->executeRequest(fn () => $this->getNewInstance()->crm()->...);\n *\n * @template T\n * @param callable(): T $apiCall\n * @return T\n *\n * @throws RateLimitException\n */\n private function executeRequest(callable $apiCall)\n {\n $cacheKey = $this->getRateLimitCacheKey();\n\n $cachedRetryAfter = Redis::get($cacheKey);\n if (is_string($cachedRetryAfter) && is_numeric($cachedRetryAfter)) {\n throw new RateLimitException(\n 'Hubspot rate limit (cached circuit-breaker)',\n (int) $cachedRetryAfter,\n );\n }\n\n try {\n return $apiCall();\n } catch (Throwable $e) {\n if ($this->isHubspotRateLimit($e)) {\n $retryAfter = $this->parseRetryAfter($e);\n\n Redis::setex($cacheKey, $retryAfter, (string) $retryAfter);\n\n $this->log->warning('[Hubspot] Received 429 from API', [\n 'team_id' => $this->config->team_id,\n 'config_id' => $this->config->getId(),\n 'retry_after' => $retryAfter,\n 'message' => $this->parseMessage($e),\n 'reason' => $e->getMessage(),\n ]);\n\n throw new RateLimitException('Hubspot returned 429', $retryAfter, $e);\n }\n\n throw $e;\n }\n }\n\n private function getRateLimitCacheKey(): string\n {\n return sprintf('hubspot:ratelimit:portal:%d', $this->config->getId());\n }\n\n public function isHubspotRateLimit(Throwable $e): bool\n {\n if ($e instanceof BadRequest\n || $e instanceof DealApiException\n || $e instanceof ContactApiException\n || $e instanceof CompanyApiException\n || $e instanceof \\GuzzleHttp\\Exception\\RequestException\n ) {\n return (int) $e->getCode() === 429;\n }\n\n return false;\n }\n\n public function parseRetryAfter(Throwable $e): int\n {\n if (method_exists($e, 'getResponseHeaders')) {\n $headers = $e->getResponseHeaders() ?: [];\n $value = $headers['Retry-After'] ?? $headers['retry-after'] ?? null;\n if (is_array($value)) {\n $value = $value[0] ?? null;\n }\n if (is_numeric($value)) {\n return (int) $value;\n }\n }\n\n $message = strtolower($this->parseMessage($e));\n\n if (str_contains($message, 'daily')) {\n return 600;\n }\n if (str_contains($message, 'ten secondly')) {\n return 10;\n }\n if (str_contains($message, 'secondly')) {\n return 1;\n }\n\n $this->log->warning('[Hubspot] No retry-after header or known message, using default', [\n 'exception_class' => get_class($e),\n 'message' => $message,\n ]);\n\n return 10;\n }\n\n public function parseMessage(Throwable $e): string\n {\n if (! method_exists($e, 'getResponseBody')) {\n return '';\n }\n\n $body = $e->getResponseBody();\n if (is_string($body)) {\n $body = json_decode($body, true) ?? [];\n }\n\n if (! is_array($body)) {\n return '';\n }\n\n return (string) ($body['message'] ?? '');\n }\n\n public function getMinimumApiVersion(): string\n {\n return self::MIN_API_VERSION;\n }\n\n public function getInstance(): Factory\n {\n return new Factory([\n 'key' => $this->accessToken,\n 'oauth2' => true,\n 'base_url' => $this->baseUrl,\n ]);\n }\n\n public function getNewInstance(): Discovery\n {\n return \\HubSpot\\Factory::createWithAccessToken($this->accessToken);\n }\n\n /**\n * Secondly and daily limits for Hubspot API\n *\n * Product Tier: Free & Starter | Professional & Enterprise | API add-on (any tier)\n * Burst: 100/10 seconds | 150/10 seconds | 200/10 seconds\n * Daily: 250,000 | 500,000 | 1,000,000\n *\n * Official documentation states: The search endpoints are rate limited to five requests per second.\n * Since with 5 RPS were still hitting secondly rate limits we lowered it to 4\n */\n public function getPaginatedData(array $payload, string $type, int $offset = 0): array\n {\n $total = 0;\n $lastId = null;\n $rows = [];\n foreach ($this->getPaginatedDataGenerator($payload, $type, $offset, $total, $lastId) as $row) {\n $rows[] = $row;\n }\n\n return ['results' => $rows, 'total' => $total, 'last_record' => $lastId];\n }\n\n /**\n * @throws HubspotException\n * @throws SocialAccountTokenInvalidException\n * @throws BadRequest\n */\n public function getPaginatedDataGenerator(\n array $payload,\n string $type,\n int $offset = 0,\n int &$total = 0,\n ?string &$lastRecordId = null\n ): \\Generator {\n return $this->paginationService->getPaginatedDataGenerator(\n $this,\n $payload,\n $type,\n $offset,\n $total,\n $lastRecordId\n );\n }\n\n /**\n * Execute a search request against HubSpot CRM objects with rate limiting.\n *\n * @param string $objectType The object type ('deals', 'companies', 'contacts', 'calls')\n * @param array<string, mixed> $payload The search payload with filters, sorts, properties, etc.\n * @return array The search response with 'results', 'total', 'paging' keys\n * @throws RateLimitException When rate limit is hit\n * @throws HubspotException On API errors\n */\n public function search(string $objectType, array $payload): array\n {\n $endpoint = self::BASE_URL . \"/crm/v3/objects/{$objectType}/search\";\n\n return $this->executeRequest(function () use ($endpoint, $payload) {\n $response = $this->getInstance()->getClient()->request('POST', $endpoint, ['json' => $payload]);\n\n return $response->toArray();\n });\n }\n\n /**\n * @throws DealApiException\n * @throws CrmException\n */\n public function getOpportunityById(string $crmId, array $fields): array\n {\n try {\n// $deal = $this->executeRequest(fn () => $this->getNewInstance()->crm()->deals()->basicApi()->getById(\n $deal = $this->getNewInstance()->crm()->deals()->basicApi()->getById(\n $crmId,\n implode(',', $fields),\n 'companies,contacts'\n );\n } catch (DealApiException $e) {\n $this->log->info('[Hubspot] Failed to fetch opportunity', [\n 'crm_id' => $crmId,\n 'reason' => $e->getMessage(),\n ]);\n\n throw $e;\n }\n\n if (! $deal instanceof DealWithAssociations) {\n throw new CrmException('Deal not found');\n }\n\n return [\n 'id' => $deal->getId(),\n 'properties' => $deal->getProperties(),\n 'associations' => $deal->getAssociations(),\n ];\n }\n\n /**\n * Generic batch read method for HubSpot objects\n *\n * @param string $objectType The object type ('deals', 'companies', 'contacts')\n * @param array<string> $crmIds Array of HubSpot object IDs (max 100)\n * @param array<string> $fields Array of property names to fetch\n *\n * @return array<string, array> Array keyed by CRM ID with object data\n */\n private function batchReadObjects(string $objectType, array $crmIds, array $fields): array\n {\n if (empty($crmIds)) {\n return [];\n }\n\n $this->validateBatchSize($objectType, $crmIds);\n $this->ensureValidToken();\n\n try {\n $batchConfig = $this->createBatchConfiguration($objectType);\n $batchReadRequest = $this->prepareBatchRequest($batchConfig, $crmIds, $fields);\n $response = $batchConfig['api']->read($batchReadRequest);\n\n $this->validateApiResponse($response, $objectType);\n\n $results = $this->processApiResults($response);\n $this->logBatchResults($objectType, $crmIds, $results);\n\n return $results;\n } catch (\\Throwable $e) {\n $this->handleBatchError($e, $objectType, $crmIds);\n }\n }\n\n private function validateBatchSize(string $objectType, array $crmIds): void\n {\n if (count($crmIds) > 100) {\n throw new \\InvalidArgumentException(\"Batch size cannot exceed 100 {$objectType}\");\n }\n }\n\n private function createBatchConfiguration(string $objectType): array\n {\n $configurations = [\n 'deals' => [\n 'batchReadRequest' => new \\HubSpot\\Client\\Crm\\Deals\\Model\\BatchReadInputSimplePublicObjectId(),\n 'inputClass' => \\HubSpot\\Client\\Crm\\Deals\\Model\\SimplePublicObjectId::class,\n 'api' => $this->getNewInstance()->crm()->deals()->batchApi(),\n ],\n 'companies' => [\n 'batchReadRequest' => new \\HubSpot\\Client\\Crm\\Companies\\Model\\BatchReadInputSimplePublicObjectId(),\n 'inputClass' => \\HubSpot\\Client\\Crm\\Companies\\Model\\SimplePublicObjectId::class,\n 'api' => $this->getNewInstance()->crm()->companies()->batchApi(),\n ],\n 'contacts' => [\n 'batchReadRequest' => new \\HubSpot\\Client\\Crm\\Contacts\\Model\\BatchReadInputSimplePublicObjectId(),\n 'inputClass' => \\HubSpot\\Client\\Crm\\Contacts\\Model\\SimplePublicObjectId::class,\n 'api' => $this->getNewInstance()->crm()->contacts()->batchApi(),\n ],\n ];\n\n if (! isset($configurations[$objectType])) {\n throw new \\InvalidArgumentException(\"Unsupported object type: {$objectType}\");\n }\n\n return $configurations[$objectType];\n }\n\n private function prepareBatchRequest(array $batchConfig, array $crmIds, array $fields): object\n {\n $batchReadRequest = $batchConfig['batchReadRequest'];\n $inputClass = $batchConfig['inputClass'];\n\n $inputs = array_map(function ($crmId) use ($inputClass) {\n $input = new $inputClass();\n $input->setId($crmId);\n\n return $input;\n }, $crmIds);\n\n $batchReadRequest->setInputs($inputs);\n $batchReadRequest->setProperties($fields);\n\n return $batchReadRequest;\n }\n\n private function validateApiResponse($response, string $objectType): void\n {\n if (! $response) {\n throw new CrmException(\"HubSpot API returned null response for {$objectType} batch read\");\n }\n }\n\n private function processApiResults($response): array\n {\n $results = [];\n $responseResults = $response->getResults();\n\n if ($responseResults) {\n foreach ($responseResults as $object) {\n if ($object && $object->getId()) {\n $results[$object->getId()] = [\n 'id' => $object->getId(),\n 'properties' => $object->getProperties() ?: [],\n ];\n }\n }\n }\n\n return $results;\n }\n\n private function logBatchResults(string $objectType, array $crmIds, array $results): void\n {\n $this->log->info(\"[HubSpot] Batch fetched {$objectType}\", [\n 'requested_count' => count($crmIds),\n 'returned_count' => count($results),\n 'crm_ids' => $crmIds,\n ]);\n }\n\n private function handleBatchError(\\Throwable $e, string $objectType, array $crmIds): void\n {\n $errorMessage = $e->getMessage() ?: 'Unknown error';\n $errorTrace = $e->getTraceAsString() ?: 'No trace available';\n\n $this->log->error(\"[HubSpot] Failed to batch fetch {$objectType}\", [\n 'crm_ids' => $crmIds,\n 'error' => $errorMessage,\n 'trace' => $errorTrace,\n ]);\n\n throw new CrmException(\"Failed to batch fetch {$objectType}: \" . $errorMessage);\n }\n\n /**\n * Batch read multiple opportunities by their CRM IDs\n *\n * @param array<string> $crmIds Array of HubSpot deal IDs (max 100)\n * @param array<string> $fields Array of property names to fetch\n *\n * @return array<string, array> Array keyed by CRM ID with opportunity data\n */\n public function getOpportunitiesByIds(array $crmIds, array $fields): array\n {\n return $this->batchReadObjects('deals', $crmIds, $fields);\n }\n\n /**\n * Batch read multiple companies by their CRM IDs\n *\n * @param array<string> $crmIds Array of HubSpot company IDs (max 100)\n * @param array<string> $fields Array of property names to fetch\n *\n * @return array<string, array> Array keyed by CRM ID with company data\n */\n public function getCompaniesByIds(array $crmIds, array $fields): array\n {\n return $this->batchReadObjects('companies', $crmIds, $fields);\n }\n\n /**\n * Batch read multiple contacts by their CRM IDs\n *\n * @param array<string> $crmIds Array of HubSpot contact IDs (max 100)\n * @param array<string> $fields Array of property names to fetch\n *\n * @return array<string, array> Array keyed by CRM ID with contact data\n */\n public function getContactsByIds(array $crmIds, array $fields): array\n {\n return $this->batchReadObjects('contacts', $crmIds, $fields);\n }\n\n /**\n * @throws CompanyApiException\n * @throws CrmException\n */\n public function getAccountById(string $crmId, array $fields): array\n {\n try {\n $company = $this->getNewInstance()->crm()->companies()->basicApi()->getById(\n $crmId,\n implode(',', $fields),\n );\n } catch (CompanyApiException $e) {\n $this->log->info('[Hubspot] Failed to fetch account', [\n 'crm_id' => $crmId,\n 'reason' => $e->getMessage(),\n ]);\n\n throw $e;\n }\n\n if (! $company instanceof CompaniesWithAssociations) {\n throw new CrmException('Account not found');\n }\n\n return [\n 'id' => $company->getId(),\n 'properties' => $company->getProperties(),\n ];\n }\n\n /**\n * @throws ContactApiException\n * @throws CrmException\n */\n public function getContactById(string $crmId, array $fields): array\n {\n try {\n $contact = $this->getNewInstance()->crm()->contacts()->basicApi()->getById(\n $crmId,\n implode(',', $fields)\n );\n } catch (ContactApiException $e) {\n $this->log->info('[Hubspot] Failed to fetch contact', [\n 'crm_id' => $crmId,\n 'reason' => $e->getMessage(),\n ]);\n\n throw $e;\n }\n\n if (! $contact instanceof ContactsWithAssociations) {\n throw new CrmException('Contact not found');\n }\n\n return [\n 'id' => $contact->getId(),\n 'properties' => $contact->getProperties(),\n ];\n }\n\n /**\n * This is email search request that Hubspot offers as GET (more generous quota)\n */\n public function getContactByEmail(string $email, array $fields = []): array\n {\n try {\n $contact = $this->getNewInstance()->crm()->contacts()->basicApi()->getById(\n $email,\n implode(',', $fields),\n null,\n false,\n 'email'\n );\n\n return [\n 'id' => $contact->getId(),\n 'properties' => $contact->getProperties(),\n ];\n } catch (ContactApiException $e) {\n $this->log->info('[Hubspot] Failed to fetch contact', [\n 'email' => $email,\n 'reason' => $e->getMessage(),\n ]);\n\n return [];\n }\n }\n\n /**\n * @throws CrmException\n */\n public function fetchProperty(string $objectType, string $propertyId): Property\n {\n $result = $this->getNewInstance()->crm()->properties()->coreApi()->getByName($objectType, $propertyId);\n\n if (! $result instanceof Property) {\n $this->log->error('[Hubspot] Failed to fetch property', [\n 'object_type' => $objectType,\n 'property_id' => $propertyId,\n 'reason' => $result->getMessage(),\n ]);\n\n throw new CrmException('Failed to fetch property');\n }\n\n return $result;\n }\n\n /**\n * @return array<CrmFieldOption>\n */\n public function fetchPropertyOptions(string $objectType, string $propertyId): array\n {\n /** @var array<CrmFieldOption> */\n return $this->fetchProperty($objectType, $propertyId)->getOptions();\n }\n\n /**\n * @return array<array{id:string, label:string, deleted:bool}>\n */\n public function fetchCallDispositions(): array\n {\n /** @var Response $response */\n $response = $this->getInstance()->engagements()->getCallDispositions();\n\n /**\n * @var array<array{\n * id:string,\n * label:string,\n * deleted: bool\n * }>\n */\n return $response->toArray();\n }\n\n /**\n * @return array<CrmFieldOption>\n */\n public function fetchOpportunityPipelineStages(): array\n {\n $stages = [];\n $apiResponse = $this->getNewInstance()->crm()->pipelines()->pipelinesApi()->getAll('deals');\n\n if ($apiResponse instanceof Error) {\n $this->log->error('[Hubspot] Failed to fetch opportunity pipelines', [\n 'reason' => $apiResponse->getMessage(),\n ]);\n\n return [];\n }\n\n foreach ($apiResponse->getResults() as $pipeline) {\n $pipelineStages = array_map(\n static function (PipelineStage $stage) {\n return [\n 'id' => $stage->getId(),\n 'label' => $stage->getLabel(),\n ];\n },\n $pipeline->getStages()\n );\n\n $stages = array_merge($stages, $pipelineStages);\n }\n\n return $stages;\n }\n\n public function fetchOpportunityPipelines(): array\n {\n $pipelines = [];\n\n try {\n $apiResponse = $this->makeRequest('/crm/v3/pipelines/deals');\n } catch (\\Exception $e) {\n $this->log->info('[Hubspot] Failed to fetch opportunity pipelines', [\n 'reason' => $e->getMessage(),\n ]);\n\n return [];\n }\n\n $response = $apiResponse->toArray();\n\n foreach ($response['results'] as $pipeline) {\n $pipelines[] = [\n 'id' => $pipeline['id'],\n 'label' => $pipeline['label'],\n ];\n }\n\n return $pipelines;\n }\n\n /**\n * @return array<CrmFieldOption>\n */\n public function fetchMeetingOutcomeFieldOptions(Field $field): array\n {\n return $field->getCrmProviderId() === 'meetingOutcome'\n ? $this->fetchMeetingOutcomeTypes()\n : $this->fetchCallActivityTypes();\n }\n\n public function fetchMeetingOutcomeTypes(): array\n {\n return $this->extractMeetingTypeOptions(\n 'https://api.hubapi.com/crm/v3/properties/meeting/hs_meeting_outcome'\n );\n }\n\n public function fetchCallActivityTypes(): array\n {\n return $this->extractMeetingTypeOptions(\n 'https://api.hubapi.com/crm/v3/properties/call/hs_activity_type'\n );\n }\n\n private function extractMeetingTypeOptions(string $endpoint): array\n {\n /** @var Response $response */\n $response = $this->getInstance()\n ->getClient()\n ->request('GET', $endpoint);\n\n /**\n * @var array<array{\n * value: string,\n * label: string,\n * displayOrder: int\n * }> $optionData\n */\n $optionData = $response->toArray()['options'] ?? [];\n\n $options = [];\n foreach ($optionData as $item) {\n $options[] = [\n 'id' => $item['value'],\n 'value' => $item['value'],\n 'label' => $item['label'],\n 'display_order' => $item['displayOrder'],\n ];\n }\n\n return $options;\n }\n\n /**\n * @return array<CrmFieldOption>\n */\n public function fetchDispositionFieldOptions(): array\n {\n $options = [];\n\n $dispositions = $this->fetchCallDispositions();\n\n foreach ($dispositions as $disposition) {\n if ($disposition['deleted'] !== false) {\n continue;\n }\n\n $option['value'] = $disposition['id'];\n $option['id'] = $disposition['id'];\n $option['label'] = $disposition['label'];\n\n $options[] = $option;\n }\n\n return $options;\n }\n\n /**\n * @return array<CrmFieldOption>\n */\n public function fetchOpportunityFieldOptions(Field $field): array\n {\n if ($field->isStageField()) {\n return $this->fetchOpportunityPipelineStages();\n }\n\n if ($field->isPipelineField()) {\n return $this->fetchOpportunityPipelines();\n }\n\n return $this->fetchPropertyOptions('deals', $field->getCrmProviderId());\n }\n\n /**\n * @throws BadRequest\n * @throws HubspotException\n */\n public function makeRequest(string $endpoint, $method = 'GET', $payload = [], ?string $queryString = null)\n {\n $endpoint = self::BASE_URL . $endpoint;\n\n if ($method === 'GET') {\n return $this->getInstance()->getClient()?->request(\n method: $method,\n endpoint: $endpoint,\n query_string: $queryString\n );\n } else {\n return $this->getInstance()->getClient()->request($method, $endpoint, [\n 'json' => ($payload),\n ]);\n }\n }\n\n /**\n * @throws BadRequest\n * @throws HubspotException\n */\n public function createMeeting(array $payload): Response\n {\n $endpoint = '/crm/v3/objects/meetings';\n\n return $this->makeRequest($endpoint, 'POST', $payload);\n }\n\n /**\n * @throws BadRequest\n * @throws HubspotException\n */\n public function updateMeeting(string $meetingId, array $payload): Response\n {\n $endpoint = '/crm/v3/objects/meetings/' . $meetingId;\n\n return $this->makeRequest($endpoint, 'PATCH', $payload);\n }\n\n /**\n * @throws \\Exception\n */\n public function createNote(\n string $body,\n string $ownerId,\n int $timestamp,\n string $objectId,\n NoteObject $noteObject\n ): ?string {\n try {\n $noteInput = new SimplePublicObjectInput([\n 'properties' => [\n 'hs_note_body' => $body,\n 'hubspot_owner_id' => $ownerId,\n 'hs_timestamp' => $timestamp,\n ],\n ]);\n\n // Create note\n $note = $this->getNewInstance()->crm()->objects()->basicApi()->create('note', $noteInput);\n\n $this->getNewInstance()->crm()->objects()->associationsApi()->create(\n 'note',\n $note->getId(),\n $this->getNoteObject($noteObject),\n $objectId,\n $this->getNoteAssociationType($noteObject),\n );\n\n return $note->getId();\n } catch (\\Exception $e) {\n $this->log->error('[Hubspot] Failed to create note', [\n 'objectId' => $objectId,\n 'noteObject' => $noteObject->getObjectType(),\n 'reason' => $e->getMessage(),\n ]);\n\n \\Sentry::captureException($e);\n }\n\n return null;\n }\n\n public function updateEngagement(string $objectId, array $engagement, array $metadata): void\n {\n $this->getInstance()->engagements()->update($objectId, $engagement, $metadata);\n }\n\n public function getEngagementData(string $engagementId): array\n {\n $engagement = $this->getInstance()->engagements()->get($engagementId);\n\n return $engagement->toArray();\n }\n\n public function createEngagement(array $engagement, array $associations, array $metadata): Response\n {\n return $this->getInstance()\n ->engagements()\n ->create($engagement, $associations, $metadata);\n }\n\n public function isUnauthorizedException(\\Exception $e): bool\n {\n // Check for specific HubSpot API exception types first\n if ($e instanceof BadRequest) {\n // BadRequest can contain 401 status codes\n return $e->getCode() === 401;\n }\n\n // Check for HTTP client exceptions with status codes\n if ($e instanceof \\GuzzleHttp\\Exception\\RequestException && $e->hasResponse()) {\n $response = $e->getResponse();\n if ($response !== null) {\n return $response->getStatusCode() === 401;\n }\n }\n\n // Check for Guzzle HTTP exceptions\n if ($e instanceof \\GuzzleHttp\\Exception\\ClientException) {\n return $e->getCode() === 401;\n }\n\n // Fallback to string matching as last resort, but be more specific\n $message = strtolower($e->getMessage());\n\n return str_contains($message, '401 unauthorized') ||\n str_contains($message, 'http 401') ||\n str_contains($message, 'status code 401') ||\n (preg_match('/\\b401\\b/', $message) && str_contains($message, 'unauthorized'));\n }\n\n /**\n * Validates and refreshes the access token if needed before API requests.\n * This ensures long-running processes don't fail due to token expiration.\n *\n * @throws SocialAccountTokenInvalidException\n */\n public function ensureValidToken(): void\n {\n if ($this->oauthAccount === null) {\n return;\n }\n\n $newToken = $this->tokenManager->ensureValidToken($this->oauthAccount);\n if ($newToken !== null) {\n $this->accessToken = $newToken;\n }\n }\n\n public function getConfig()\n {\n return $this->config;\n }\n\n // returns only active (archived=false)\n public function getOwners(): array\n {\n return $this->getNewInstance()->crm()->owners()->getAll();\n }\n\n /**\n * @param bool $archived\n *\n * @return array<Owner>|[]\n */\n public function getOwnersArchived(bool $archived = true): array\n {\n $endpoint = '/crm/v3/owners';\n $queryParams = [\n 'archived' => $archived ? 'true' : 'false',\n ];\n $queryString = http_build_query($queryParams);\n\n $owners = [];\n\n try {\n $response = $this->makeRequest(endpoint: $endpoint, queryString: $queryString);\n $responseData = $response?->toArray();\n\n foreach ($responseData['results'] as $result) {\n try {\n $owners[] = Owner::create($result);\n } catch (Throwable $e) {\n $this->log->error('[HubSpot] Failed to process owner data', [\n 'result' => $result,\n 'error' => $e->getMessage(),\n ]);\n\n continue;\n }\n }\n } catch (Throwable $e) {\n $this->log->error('HubSpot] Failed to fetch owners', [\n 'archived' => $archived,\n 'error' => $e->getMessage(),\n ]);\n\n return [];\n }\n\n return $owners;\n }\n\n public function getMeeting(string $engagementId): ObjectWithAssociations\n {\n return $this->getNewInstance()->crm()->objects()->basicApi()\n ->getById('meeting', $engagementId, null, 'contact,company,deal');\n }\n\n public function deleteEngagement(string $engagementId): void\n {\n $this->getInstance()->engagements()->delete((int) $engagementId);\n }\n\n public function getAssociationsData(array $ids, string $fromObject, string $toObject): array\n {\n $associationData = [];\n $idChunks = array_chunk($ids, self::ASSOCIATIONS_BATCH_SIZE_LIMIT);\n\n foreach ($idChunks as $idChunk) {\n try {\n $batchInput = new \\HubSpot\\Client\\Crm\\Associations\\Model\\BatchInputPublicObjectId();\n $batchInput->setInputs(array_map(function ($id) {\n $publicObjectId = new \\HubSpot\\Client\\Crm\\Associations\\Model\\PublicObjectId();\n $publicObjectId->setId($id);\n\n return $publicObjectId;\n }, $idChunk));\n\n $associatedObjectsData = $this\n ->getNewInstance()\n ->crm()\n ->associations()\n ->batchApi()\n ->read($fromObject, $toObject, $batchInput);\n\n if ($associatedObjectsData instanceof \\HubSpot\\Client\\Crm\\Associations\\Model\\BatchResponsePublicAssociationMulti) {\n foreach ($associatedObjectsData->getResults() as $association) {\n $from = $association->getFrom()->getId();\n $toAssociations = $association->getTo();\n\n if (! empty($toAssociations)) {\n $associationData[$from] = array_map(function ($item) {\n return $item->getId();\n }, $toAssociations);\n }\n }\n }\n// } catch (RateLimitException $e) {\n// throw $e;\n } catch (\\Exception $e) {\n $this->log->error('[Hubspot] Failed to fetch associations', [\n 'from_object' => $fromObject,\n 'to_object' => $toObject,\n 'reason' => $e->getMessage(),\n ]);\n }\n }\n\n return $associationData;\n }\n\n /**\n * @throws \\Exception\n */\n private function getNoteAssociationType(NoteObject $noteObject): string\n {\n return match($noteObject) {\n NoteObject::Opportunity => 'note_to_deal',\n NoteObject::Lead, NoteObject::Contact => 'note_to_contact', // or 'note_to_lead' if your portal supports it\n NoteObject::Account => 'note_to_company',\n NoteObject::Call, NoteObject::Event => throw new \\Exception('Not supported'),\n };\n }\n\n /**\n * @throws \\Exception\n */\n private function getNoteObject(NoteObject $noteObject): string\n {\n return match($noteObject) {\n NoteObject::Opportunity => 'deal',\n NoteObject::Lead, NoteObject::Contact => 'contact',\n NoteObject::Account => 'company',\n NoteObject::Call, NoteObject::Event => throw new \\Exception('Not supported'),\n };\n }\n\n public function addAssociations(string $objectType, string $associationType, array $payload): Response\n {\n $endpoint = \"/crm/v4/associations/$objectType/$associationType/batch/create\";\n\n return $this->makeRequest($endpoint, 'POST', $payload);\n }\n\n public function removeAssociations(string $objectType, string $associationType, array $payload): Response\n {\n $endpoint = \"/crm/v4/associations/$objectType/$associationType/batch/archive\";\n\n return $this->makeRequest($endpoint, 'POST', $payload);\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":true,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"19","depth":4,"bounds":{"left":0.7124335,"top":0.10055866,"width":0.009640957,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.7237367,"top":0.09896249,"width":0.00731383,"height":0.018355945},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.73105055,"top":0.09896249,"width":0.006981383,"height":0.018355945},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"[2026-05-07 14:21:15] local.INFO: [Hubspot] DEBUG Getting headers {\n\"headers\":{\n\"Date\":[\"Thu,07 May 2026 14:21:15 GMT\"],\n \"Content-Type\":[\"application/json;charset=utf-8\"],\n \"Transfer-Encoding\":[\"chunked\"],\n \"Connection\":[\"keep-alive\"],\n \"CF-Ray\":[\"9f80deb8db60dc3a-SOF\"],\n \"CF-Cache-Status\":[\"DYNAMIC\"],\n \"Strict-Transport-Security\":[\"max-age=31536000; includeSubDomains; preload\"],\n \"Vary\":[\"origin,\n accept-encoding\"],\n \"access-control-allow-credentials\":[\"false\"],\n \"server-timing\":[\"hcid;desc=\\\"019e02d0-6fd8-7812-bdba-885b7ccb3ee3\\\",\n cfr;desc=\\\"9f80deb8e7c6dc3a-IAD\\\"\"],\n \"x-content-type-options\":[\"nosniff\"],\n \"x-hubspot-correlation-id\":[\"019e02d0-6fd8-7812-bdba-885b7ccb3ee3\"],\n \"Set-Cookie\":[\"__cf_bm=SIUrtdQgXVrik50pdqF6hZVYKhzTnQBidvMabeCtm0Y-1778163675-1.0.1.1-rI.ZggtDKxTge5zr8_2gbBfWMQQ.ufZEXDZyHz2mBUFdzdo2gTHEsOkXMSEShjK0hGYxNhUGM1ZoBpX7BcFZcHEjA7Cs_.SMUhUnd2nYjko; path=/; expires=Thu,\n 07-May-26 14:51:15 GMT; domain=.hubapi.com; HttpOnly; Secure; SameSite=None\"],\n \"Report-To\":[\"{\n\\\"endpoints\\\":[{\n\\\"url\\\":\\\"https:\\\\/\\\\/a.nel.cloudflare.com\\\\/report\\\\/v4?s=NYAlsVTP0fYm32qrSDjxYE4sd2RWRqiSp3wHsmdEgZlzoYdxI%2BIxVpHmsKn3O%2BKVA3mFIJ2m7YRECDGSM%2BW2IYTzo6FM4%2BdUIjURO8srzKSvJgZ%2BQ6R79arKQw3uHLlX\\\"}],\n\\\"group\\\":\\\"cf-nel\\\",\n\\\"max_age\\\":604800}\"],\n\"NEL\":[\"{\n\\\"success_fraction\\\":0.01,\n\\\"report_to\\\":\\\"cf-nel\\\",\n\\\"max_age\\\":604800}\"],\n\"Server\":[\"cloudflare\"]}} {\n\"correlation_id\":\"95236535-ec98-4541-b92a-adfa73b69eab\",\n\"trace_id\":\"c7ab8365-903f-46d4-9403-0e5b551e3545\"}","depth":4,"bounds":{"left":0.47672874,"top":0.09736632,"width":0.52327126,"height":0.8818835},"on_screen":true,"lines":[{"char_start":207,"char_count":30,"bounds":{"left":0.47672874,"top":0.0,"width":0.07513298,"height":0.014365523}},{"char_start":237,"char_count":36,"bounds":{"left":0.47672874,"top":0.0,"width":0.09075798,"height":0.014365523}},{"char_start":273,"char_count":32,"bounds":{"left":0.47672874,"top":0.0,"width":0.080119684,"height":0.014365523}},{"char_start":305,"char_count":79,"bounds":{"left":0.47672874,"top":0.0,"width":0.20212767,"height":0.014365523}},{"char_start":384,"char_count":18,"bounds":{"left":0.47672874,"top":0.0,"width":0.043882977,"height":0.014365523}},{"char_start":402,"char_count":21,"bounds":{"left":0.47672874,"top":0.0,"width":0.051861703,"height":0.014365523}},{"char_start":423,"char_count":48,"bounds":{"left":0.47672874,"top":0.008778931,"width":0.12167553,"height":0.014365523}},{"char_start":471,"char_count":72,"bounds":{"left":0.47672874,"top":0.026336791,"width":0.18384309,"height":0.014365523}},{"char_start":543,"char_count":40,"bounds":{"left":0.47672874,"top":0.043894652,"width":0.10106383,"height":0.014365523}},{"char_start":583,"char_count":41,"bounds":{"left":0.47672874,"top":0.061452515,"width":0.10372341,"height":0.014365523}},{"char_start":624,"char_count":72,"bounds":{"left":0.47672874,"top":0.079010375,"width":0.18384309,"height":0.014365523}},{"char_start":696,"char_count":219,"bounds":{"left":0.47672874,"top":0.096568234,"width":0.52327126,"height":0.014365523}},{"char_start":915,"char_count":83,"bounds":{"left":0.47672874,"top":0.11412609,"width":0.21243352,"height":0.014365523}},{"char_start":998,"char_count":20,"bounds":{"left":0.47672874,"top":0.13168396,"width":0.04920213,"height":0.014365523}},{"char_start":1018,"char_count":17,"bounds":{"left":0.47672874,"top":0.14924182,"width":0.041223403,"height":0.014365523}},{"char_start":1035,"char_count":203,"bounds":{"left":0.47672874,"top":0.16679968,"width":0.52327126,"height":0.014365523}},{"char_start":1238,"char_count":22,"bounds":{"left":0.47672874,"top":0.18435754,"width":0.05418883,"height":0.014365523}},{"char_start":1260,"char_count":23,"bounds":{"left":0.47672874,"top":0.2019154,"width":0.056848403,"height":0.014365523}},{"char_start":1283,"char_count":10,"bounds":{"left":0.47672874,"top":0.21947326,"width":0.023271276,"height":0.014365523}},{"char_start":1293,"char_count":27,"bounds":{"left":0.47672874,"top":0.23703113,"width":0.06715426,"height":0.014365523}},{"char_start":1320,"char_count":26,"bounds":{"left":0.47672874,"top":0.254589,"width":0.06482713,"height":0.014365523}},{"char_start":1346,"char_count":23,"bounds":{"left":0.47672874,"top":0.27214685,"width":0.056848403,"height":0.014365523}},{"char_start":1369,"char_count":28,"bounds":{"left":0.47672874,"top":0.2897047,"width":0.06981383,"height":0.014365523}},{"char_start":1397,"char_count":57,"bounds":{"left":0.47672874,"top":0.30726257,"width":0.14494681,"height":0.014365523}}],"value":"[2026-05-07 14:21:15] local.INFO: [Hubspot] DEBUG Getting headers {\n\"headers\":{\n\"Date\":[\"Thu,07 May 2026 14:21:15 GMT\"],\n \"Content-Type\":[\"application/json;charset=utf-8\"],\n \"Transfer-Encoding\":[\"chunked\"],\n \"Connection\":[\"keep-alive\"],\n \"CF-Ray\":[\"9f80deb8db60dc3a-SOF\"],\n \"CF-Cache-Status\":[\"DYNAMIC\"],\n \"Strict-Transport-Security\":[\"max-age=31536000; includeSubDomains; preload\"],\n \"Vary\":[\"origin,\n accept-encoding\"],\n \"access-control-allow-credentials\":[\"false\"],\n \"server-timing\":[\"hcid;desc=\\\"019e02d0-6fd8-7812-bdba-885b7ccb3ee3\\\",\n cfr;desc=\\\"9f80deb8e7c6dc3a-IAD\\\"\"],\n \"x-content-type-options\":[\"nosniff\"],\n \"x-hubspot-correlation-id\":[\"019e02d0-6fd8-7812-bdba-885b7ccb3ee3\"],\n \"Set-Cookie\":[\"__cf_bm=SIUrtdQgXVrik50pdqF6hZVYKhzTnQBidvMabeCtm0Y-1778163675-1.0.1.1-rI.ZggtDKxTge5zr8_2gbBfWMQQ.ufZEXDZyHz2mBUFdzdo2gTHEsOkXMSEShjK0hGYxNhUGM1ZoBpX7BcFZcHEjA7Cs_.SMUhUnd2nYjko; path=/; expires=Thu,\n 07-May-26 14:51:15 GMT; domain=.hubapi.com; HttpOnly; Secure; SameSite=None\"],\n \"Report-To\":[\"{\n\\\"endpoints\\\":[{\n\\\"url\\\":\\\"https:\\\\/\\\\/a.nel.cloudflare.com\\\\/report\\\\/v4?s=NYAlsVTP0fYm32qrSDjxYE4sd2RWRqiSp3wHsmdEgZlzoYdxI%2BIxVpHmsKn3O%2BKVA3mFIJ2m7YRECDGSM%2BW2IYTzo6FM4%2BdUIjURO8srzKSvJgZ%2BQ6R79arKQw3uHLlX\\\"}],\n\\\"group\\\":\\\"cf-nel\\\",\n\\\"max_age\\\":604800}\"],\n\"NEL\":[\"{\n\\\"success_fraction\\\":0.01,\n\\\"report_to\\\":\\\"cf-nel\\\",\n\\\"max_age\\\":604800}\"],\n\"Server\":[\"cloudflare\"]}} {\n\"correlation_id\":\"95236535-ec98-4541-b92a-adfa73b69eab\",\n\"trace_id\":\"c7ab8365-903f-46d4-9403-0e5b551e3545\"}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"bounds":{"left":0.011968086,"top":0.047885075,"width":0.024268618,"height":0.024740623},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
5549352204577814028
|
6378757150172121188
|
idle
|
accessibility
|
NULL
|
Project: faVsco.js, menu
JY-20725-handle-HS-search Project: faVsco.js, menu
JY-20725-handle-HS-search-rate-limit, menu
Start Listening for PHP Debug Connections
HandleHubspotRateLimitTest
Run 'HandleHubspotRateLimitTest'
Debug 'HandleHubspotRateLimitTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
2
67
3
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Services\Crm\Hubspot;
use HubSpot\Client\Crm\Deals\ApiException as DealApiException;
use HubSpot\Client\Crm\Contacts\ApiException as ContactApiException;
use HubSpot\Client\Crm\Companies\ApiException as CompanyApiException;
use HubSpot\Client\Crm\Contacts\Model\SimplePublicObjectWithAssociations as ContactsWithAssociations;
use HubSpot\Client\Crm\Companies\Model\SimplePublicObjectWithAssociations as CompaniesWithAssociations;
use HubSpot\Client\Crm\Deals\Model\SimplePublicObjectWithAssociations as DealWithAssociations;
use HubSpot\Client\Crm\Objects\Model\SimplePublicObjectInput;
use HubSpot\Client\Crm\Objects\Model\SimplePublicObjectWithAssociations as ObjectWithAssociations;
use HubSpot\Client\Crm\Pipelines\Model\Error;
use HubSpot\Client\Crm\Pipelines\Model\PipelineStage;
use HubSpot\Client\Crm\Properties\Model\Property;
use HubSpot\Discovery\Discovery;
use Jiminny\Exceptions\CrmException;
use Jiminny\Exceptions\RateLimitException;
use Jiminny\Exceptions\SocialAccountTokenInvalidException;
use Jiminny\Jobs\Crm\NoteObject;
use Jiminny\Models\Crm\Field;
use Jiminny\Services\Crm\BaseClient;
use Jiminny\Services\Crm\Hubspot\DTO\Response\Owner;
use Jiminny\Services\SocialAccountService;
use SevenShores\Hubspot\Exceptions\BadRequest;
use SevenShores\Hubspot\Exceptions\HubspotException;
use SevenShores\Hubspot\Factory;
use SevenShores\Hubspot\Http\Response;
use Jiminny\Services\Crm\Hubspot\Pagination\HubspotPaginationService;
use Illuminate\Support\Facades\Redis;
use Throwable;
/**
* @phpstan-type CrmFieldOption array{id:string, label:string, value?:string}
*/
class Client extends BaseClient implements HubspotClientInterface
{
public const string MIN_API_VERSION = '2';
public const string BASE_URL = '[URL_WITH_CREDENTIALS] T
* @param callable(): T $apiCall
* @return T
*
* @throws RateLimitException
*/
private function executeRequest(callable $apiCall)
{
$cacheKey = $this->getRateLimitCacheKey();
$cachedRetryAfter = Redis::get($cacheKey);
if (is_string($cachedRetryAfter) && is_numeric($cachedRetryAfter)) {
throw new RateLimitException(
'Hubspot rate limit (cached circuit-breaker)',
(int) $cachedRetryAfter,
);
}
try {
return $apiCall();
} catch (Throwable $e) {
if ($this->isHubspotRateLimit($e)) {
$retryAfter = $this->parseRetryAfter($e);
Redis::setex($cacheKey, $retryAfter, (string) $retryAfter);
$this->log->warning('[Hubspot] Received 429 from API', [
'team_id' => $this->config->team_id,
'config_id' => $this->config->getId(),
'retry_after' => $retryAfter,
'message' => $this->parseMessage($e),
'reason' => $e->getMessage(),
]);
throw new RateLimitException('Hubspot returned 429', $retryAfter, $e);
}
throw $e;
}
}
private function getRateLimitCacheKey(): string
{
return sprintf('hubspot:ratelimit:portal:%d', $this->config->getId());
}
public function isHubspotRateLimit(Throwable $e): bool
{
if ($e instanceof BadRequest
|| $e instanceof DealApiException
|| $e instanceof ContactApiException
|| $e instanceof CompanyApiException
|| $e instanceof \GuzzleHttp\Exception\RequestException
) {
return (int) $e->getCode() === 429;
}
return false;
}
public function parseRetryAfter(Throwable $e): int
{
if (method_exists($e, 'getResponseHeaders')) {
$headers = $e->getResponseHeaders() ?: [];
$value = $headers['Retry-After'] ?? $headers['retry-after'] ?? null;
if (is_array($value)) {
$value = $value[0] ?? null;
}
if (is_numeric($value)) {
return (int) $value;
}
}
$message = strtolower($this->parseMessage($e));
if (str_contains($message, 'daily')) {
return 600;
}
if (str_contains($message, 'ten secondly')) {
return 10;
}
if (str_contains($message, 'secondly')) {
return 1;
}
$this->log->warning('[Hubspot] No retry-after header or known message, using default', [
'exception_class' => get_class($e),
'message' => $message,
]);
return 10;
}
public function parseMessage(Throwable $e): string
{
if (! method_exists($e, 'getResponseBody')) {
return '';
}
$body = $e->getResponseBody();
if (is_string($body)) {
$body = json_decode($body, true) ?? [];
}
if (! is_array($body)) {
return '';
}
return (string) ($body['message'] ?? '');
}
public function getMinimumApiVersion(): string
{
return self::MIN_API_VERSION;
}
public function getInstance(): Factory
{
return new Factory([
'key' => $this->accessToken,
'oauth2' => true,
'base_url' => $this->baseUrl,
]);
}
public function getNewInstance(): Discovery
{
return \HubSpot\Factory::createWithAccessToken($this->accessToken);
}
/**
* Secondly and daily limits for Hubspot API
*
* Product Tier: Free & Starter | Professional & Enterprise | API add-on (any tier)
* Burst: 100/10 seconds | 150/10 seconds | 200/10 seconds
* Daily: 250,000 | 500,000 | 1,000,000
*
* Official documentation states: The search endpoints are rate limited to five requests per second.
* Since with 5 RPS were still hitting secondly rate limits we lowered it to 4
*/
public function getPaginatedData(array $payload, string $type, int $offset = 0): array
{
$total = 0;
$lastId = null;
$rows = [];
foreach ($this->getPaginatedDataGenerator($payload, $type, $offset, $total, $lastId) as $row) {
$rows[] = $row;
}
return ['results' => $rows, 'total' => $total, 'last_record' => $lastId];
}
/**
* @throws HubspotException
* @throws SocialAccountTokenInvalidException
* @throws BadRequest
*/
public function getPaginatedDataGenerator(
array $payload,
string $type,
int $offset = 0,
int &$total = 0,
?string &$lastRecordId = null
): \Generator {
return $this->paginationService->getPaginatedDataGenerator(
$this,
$payload,
$type,
$offset,
$total,
$lastRecordId
);
}
/**
* Execute a search request against HubSpot CRM objects with rate limiting.
*
* @param string $objectType The object type ('deals', 'companies', 'contacts', 'calls')
* @param array<string, mixed> $payload The search payload with filters, sorts, properties, etc.
* @return array The search response with 'results', 'total', 'paging' keys
* @throws RateLimitException When rate limit is hit
* @throws HubspotException On API errors
*/
public function search(string $objectType, array $payload): array
{
$endpoint = self::BASE_URL . "/crm/v3/objects/{$objectType}/search";
return $this->executeRequest(function () use ($endpoint, $payload) {
$response = $this->getInstance()->getClient()->request('POST', $endpoint, ['json' => $payload]);
return $response->toArray();
});
}
/**
* @throws DealApiException
* @throws CrmException
*/
public function getOpportunityById(string $crmId, array $fields): array
{
try {
// $deal = $this->executeRequest(fn () => $this->getNewInstance()->crm()->deals()->basicApi()->getById(
$deal = $this->getNewInstance()->crm()->deals()->basicApi()->getById(
$crmId,
implode(',', $fields),
'companies,contacts'
);
} catch (DealApiException $e) {
$this->log->info('[Hubspot] Failed to fetch opportunity', [
'crm_id' => $crmId,
'reason' => $e->getMessage(),
]);
throw $e;
}
if (! $deal instanceof DealWithAssociations) {
throw new CrmException('Deal not found');
}
return [
'id' => $deal->getId(),
'properties' => $deal->getProperties(),
'associations' => $deal->getAssociations(),
];
}
/**
* Generic batch read method for HubSpot objects
*
* @param string $objectType The object type ('deals', 'companies', 'contacts')
* @param array<string> $crmIds Array of HubSpot object IDs (max 100)
* @param array<string> $fields Array of property names to fetch
*
* @return array<string, array> Array keyed by CRM ID with object data
*/
private function batchReadObjects(string $objectType, array $crmIds, array $fields): array
{
if (empty($crmIds)) {
return [];
}
$this->validateBatchSize($objectType, $crmIds);
$this->ensureValidToken();
try {
$batchConfig = $this->createBatchConfiguration($objectType);
$batchReadRequest = $this->prepareBatchRequest($batchConfig, $crmIds, $fields);
$response = $batchConfig['api']->read($batchReadRequest);
$this->validateApiResponse($response, $objectType);
$results = $this->processApiResults($response);
$this->logBatchResults($objectType, $crmIds, $results);
return $results;
} catch (\Throwable $e) {
$this->handleBatchError($e, $objectType, $crmIds);
}
}
private function validateBatchSize(string $objectType, array $crmIds): void
{
if (count($crmIds) > 100) {
throw new \InvalidArgumentException("Batch size cannot exceed 100 {$objectType}");
}
}
private function createBatchConfiguration(string $objectType): array
{
$configurations = [
'deals' => [
'batchReadRequest' => new \HubSpot\Client\Crm\Deals\Model\BatchReadInputSimplePublicObjectId(),
'inputClass' => \HubSpot\Client\Crm\Deals\Model\SimplePublicObjectId::class,
'api' => $this->getNewInstance()->crm()->deals()->batchApi(),
],
'companies' => [
'batchReadRequest' => new \HubSpot\Client\Crm\Companies\Model\BatchReadInputSimplePublicObjectId(),
'inputClass' => \HubSpot\Client\Crm\Companies\Model\SimplePublicObjectId::class,
'api' => $this->getNewInstance()->crm()->companies()->batchApi(),
],
'contacts' => [
'batchReadRequest' => new \HubSpot\Client\Crm\Contacts\Model\BatchReadInputSimplePublicObjectId(),
'inputClass' => \HubSpot\Client\Crm\Contacts\Model\SimplePublicObjectId::class,
'api' => $this->getNewInstance()->crm()->contacts()->batchApi(),
],
];
if (! isset($configurations[$objectType])) {
throw new \InvalidArgumentException("Unsupported object type: {$objectType}");
}
return $configurations[$objectType];
}
private function prepareBatchRequest(array $batchConfig, array $crmIds, array $fields): object
{
$batchReadRequest = $batchConfig['batchReadRequest'];
$inputClass = $batchConfig['inputClass'];
$inputs = array_map(function ($crmId) use ($inputClass) {
$input = new $inputClass();
$input->setId($crmId);
return $input;
}, $crmIds);
$batchReadRequest->setInputs($inputs);
$batchReadRequest->setProperties($fields);
return $batchReadRequest;
}
private function validateApiResponse($response, string $objectType): void
{
if (! $response) {
throw new CrmException("HubSpot API returned null response for {$objectType} batch read");
}
}
private function processApiResults($response): array
{
$results = [];
$responseResults = $response->getResults();
if ($responseResults) {
foreach ($responseResults as $object) {
if ($object && $object->getId()) {
$results[$object->getId()] = [
'id' => $object->getId(),
'properties' => $object->getProperties() ?: [],
];
}
}
}
return $results;
}
private function logBatchResults(string $objectType, array $crmIds, array $results): void
{
$this->log->info("[HubSpot] Batch fetched {$objectType}", [
'requested_count' => count($crmIds),
'returned_count' => count($results),
'crm_ids' => $crmIds,
]);
}
private function handleBatchError(\Throwable $e, string $objectType, array $crmIds): void
{
$errorMessage = $e->getMessage() ?: 'Unknown error';
$errorTrace = $e->getTraceAsString() ?: 'No trace available';
$this->log->error("[HubSpot] Failed to batch fetch {$objectType}", [
'crm_ids' => $crmIds,
'error' => $errorMessage,
'trace' => $errorTrace,
]);
throw new CrmException("Failed to batch fetch {$objectType}: " . $errorMessage);
}
/**
* Batch read multiple opportunities by their CRM IDs
*
* @param array<string> $crmIds Array of HubSpot deal IDs (max 100)
* @param array<string> $fields Array of property names to fetch
*
* @return array<string, array> Array keyed by CRM ID with opportunity data
*/
public function getOpportunitiesByIds(array $crmIds, array $fields): array
{
return $this->batchReadObjects('deals', $crmIds, $fields);
}
/**
* Batch read multiple companies by their CRM IDs
*
* @param array<string> $crmIds Array of HubSpot company IDs (max 100)
* @param array<string> $fields Array of property names to fetch
*
* @return array<string, array> Array keyed by CRM ID with company data
*/
public function getCompaniesByIds(array $crmIds, array $fields): array
{
return $this->batchReadObjects('companies', $crmIds, $fields);
}
/**
* Batch read multiple contacts by their CRM IDs
*
* @param array<string> $crmIds Array of HubSpot contact IDs (max 100)
* @param array<string> $fields Array of property names to fetch
*
* @return array<string, array> Array keyed by CRM ID with contact data
*/
public function getContactsByIds(array $crmIds, array $fields): array
{
return $this->batchReadObjects('contacts', $crmIds, $fields);
}
/**
* @throws CompanyApiException
* @throws CrmException
*/
public function getAccountById(string $crmId, array $fields): array
{
try {
$company = $this->getNewInstance()->crm()->companies()->basicApi()->getById(
$crmId,
implode(',', $fields),
);
} catch (CompanyApiException $e) {
$this->log->info('[Hubspot] Failed to fetch account', [
'crm_id' => $crmId,
'reason' => $e->getMessage(),
]);
throw $e;
}
if (! $company instanceof CompaniesWithAssociations) {
throw new CrmException('Account not found');
}
return [
'id' => $company->getId(),
'properties' => $company->getProperties(),
];
}
/**
* @throws ContactApiException
* @throws CrmException
*/
public function getContactById(string $crmId, array $fields): array
{
try {
$contact = $this->getNewInstance()->crm()->contacts()->basicApi()->getById(
$crmId,
implode(',', $fields)
);
} catch (ContactApiException $e) {
$this->log->info('[Hubspot] Failed to fetch contact', [
'crm_id' => $crmId,
'reason' => $e->getMessage(),
]);
throw $e;
}
if (! $contact instanceof ContactsWithAssociations) {
throw new CrmException('Contact not found');
}
return [
'id' => $contact->getId(),
'properties' => $contact->getProperties(),
];
}
/**
* This is email search request that Hubspot offers as GET (more generous quota)
*/
public function getContactByEmail(string $email, array $fields = []): array
{
try {
$contact = $this->getNewInstance()->crm()->contacts()->basicApi()->getById(
$email,
implode(',', $fields),
null,
false,
'email'
);
return [
'id' => $contact->getId(),
'properties' => $contact->getProperties(),
];
} catch (ContactApiException $e) {
$this->log->info('[Hubspot] Failed to fetch contact', [
'email' => $email,
'reason' => $e->getMessage(),
]);
return [];
}
}
/**
* @throws CrmException
*/
public function fetchProperty(string $objectType, string $propertyId): Property
{
$result = $this->getNewInstance()->crm()->properties()->coreApi()->getByName($objectType, $propertyId);
if (! $result instanceof Property) {
$this->log->error('[Hubspot] Failed to fetch property', [
'object_type' => $objectType,
'property_id' => $propertyId,
'reason' => $result->getMessage(),
]);
throw new CrmException('Failed to fetch property');
}
return $result;
}
/**
* @return array<CrmFieldOption>
*/
public function fetchPropertyOptions(string $objectType, string $propertyId): array
{
/** @var array<CrmFieldOption> */
return $this->fetchProperty($objectType, $propertyId)->getOptions();
}
/**
* @return array<array{id:string, label:string, deleted:bool}>
*/
public function fetchCallDispositions(): array
{
/** @var Response $response */
$response = $this->getInstance()->engagements()->getCallDispositions();
/**
* @var array<array{
* id:string,
* label:string,
* deleted: bool
* }>
*/
return $response->toArray();
}
/**
* @return array<CrmFieldOption>
*/
public function fetchOpportunityPipelineStages(): array
{
$stages = [];
$apiResponse = $this->getNewInstance()->crm()->pipelines()->pipelinesApi()->getAll('deals');
if ($apiResponse instanceof Error) {
$this->log->error('[Hubspot] Failed to fetch opportunity pipelines', [
'reason' => $apiResponse->getMessage(),
]);
return [];
}
foreach ($apiResponse->getResults() as $pipeline) {
$pipelineStages = array_map(
static function (PipelineStage $stage) {
return [
'id' => $stage->getId(),
'label' => $stage->getLabel(),
];
},
$pipeline->getStages()
);
$stages = array_merge($stages, $pipelineStages);
}
return $stages;
}
public function fetchOpportunityPipelines(): array
{
$pipelines = [];
try {
$apiResponse = $this->makeRequest('/crm/v3/pipelines/deals');
} catch (\Exception $e) {
$this->log->info('[Hubspot] Failed to fetch opportunity pipelines', [
'reason' => $e->getMessage(),
]);
return [];
}
$response = $apiResponse->toArray();
foreach ($response['results'] as $pipeline) {
$pipelines[] = [
'id' => $pipeline['id'],
'label' => $pipeline['label'],
];
}
return $pipelines;
}
/**
* @return array<CrmFieldOption>
*/
public function fetchMeetingOutcomeFieldOptions(Field $field): array
{
return $field->getCrmProviderId() === 'meetingOutcome'
? $this->fetchMeetingOutcomeTypes()
: $this->fetchCallActivityTypes();
}
public function fetchMeetingOutcomeTypes(): array
{
return $this->extractMeetingTypeOptions(
'[URL_WITH_CREDENTIALS] Response $response */
$response = $this->getInstance()
->getClient()
->request('GET', $endpoint);
/**
* @var array<array{
* value: string,
* label: string,
* displayOrder: int
* }> $optionData
*/
$optionData = $response->toArray()['options'] ?? [];
$options = [];
foreach ($optionData as $item) {
$options[] = [
'id' => $item['value'],
'value' => $item['value'],
'label' => $item['label'],
'display_order' => $item['displayOrder'],
];
}
return $options;
}
/**
* @return array<CrmFieldOption>
*/
public function fetchDispositionFieldOptions(): array
{
$options = [];
$dispositions = $this->fetchCallDispositions();
foreach ($dispositions as $disposition) {
if ($disposition['deleted'] !== false) {
continue;
}
$option['value'] = $disposition['id'];
$option['id'] = $disposition['id'];
$option['label'] = $disposition['label'];
$options[] = $option;
}
return $options;
}
/**
* @return array<CrmFieldOption>
*/
public function fetchOpportunityFieldOptions(Field $field): array
{
if ($field->isStageField()) {
return $this->fetchOpportunityPipelineStages();
}
if ($field->isPipelineField()) {
return $this->fetchOpportunityPipelines();
}
return $this->fetchPropertyOptions('deals', $field->getCrmProviderId());
}
/**
* @throws BadRequest
* @throws HubspotException
*/
public function makeRequest(string $endpoint, $method = 'GET', $payload = [], ?string $queryString = null)
{
$endpoint = self::BASE_URL . $endpoint;
if ($method === 'GET') {
return $this->getInstance()->getClient()?->request(
method: $method,
endpoint: $endpoint,
query_string: $queryString
);
} else {
return $this->getInstance()->getClient()->request($method, $endpoint, [
'json' => ($payload),
]);
}
}
/**
* @throws BadRequest
* @throws HubspotException
*/
public function createMeeting(array $payload): Response
{
$endpoint = '/crm/v3/objects/meetings';
return $this->makeRequest($endpoint, 'POST', $payload);
}
/**
* @throws BadRequest
* @throws HubspotException
*/
public function updateMeeting(string $meetingId, array $payload): Response
{
$endpoint = '/crm/v3/objects/meetings/' . $meetingId;
return $this->makeRequest($endpoint, 'PATCH', $payload);
}
/**
* @throws \Exception
*/
public function createNote(
string $body,
string $ownerId,
int $timestamp,
string $objectId,
NoteObject $noteObject
): ?string {
try {
$noteInput = new SimplePublicObjectInput([
'properties' => [
'hs_note_body' => $body,
'hubspot_owner_id' => $ownerId,
'hs_timestamp' => $timestamp,
],
]);
// Create note
$note = $this->getNewInstance()->crm()->objects()->basicApi()->create('note', $noteInput);
$this->getNewInstance()->crm()->objects()->associationsApi()->create(
'note',
$note->getId(),
$this->getNoteObject($noteObject),
$objectId,
$this->getNoteAssociationType($noteObject),
);
return $note->getId();
} catch (\Exception $e) {
$this->log->error('[Hubspot] Failed to create note', [
'objectId' => $objectId,
'noteObject' => $noteObject->getObjectType(),
'reason' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
return null;
}
public function updateEngagement(string $objectId, array $engagement, array $metadata): void
{
$this->getInstance()->engagements()->update($objectId, $engagement, $metadata);
}
public function getEngagementData(string $engagementId): array
{
$engagement = $this->getInstance()->engagements()->get($engagementId);
return $engagement->toArray();
}
public function createEngagement(array $engagement, array $associations, array $metadata): Response
{
return $this->getInstance()
->engagements()
->create($engagement, $associations, $metadata);
}
public function isUnauthorizedException(\Exception $e): bool
{
// Check for specific HubSpot API exception types first
if ($e instanceof BadRequest) {
// BadRequest can contain 401 status codes
return $e->getCode() === 401;
}
// Check for HTTP client exceptions with status codes
if ($e instanceof \GuzzleHttp\Exception\RequestException && $e->hasResponse()) {
$response = $e->getResponse();
if ($response !== null) {
return $response->getStatusCode() === 401;
}
}
// Check for Guzzle HTTP exceptions
if ($e instanceof \GuzzleHttp\Exception\ClientException) {
return $e->getCode() === 401;
}
// Fallback to string matching as last resort, but be more specific
$message = strtolower($e->getMessage());
return str_contains($message, '401 unauthorized') ||
str_contains($message, 'http 401') ||
str_contains($message, 'status code 401') ||
(preg_match('/\b401\b/', $message) && str_contains($message, 'unauthorized'));
}
/**
* Validates and refreshes the access token if needed before API requests.
* This ensures long-running processes don't fail due to token expiration.
*
* @throws SocialAccountTokenInvalidException
*/
public function ensureValidToken(): void
{
if ($this->oauthAccount === null) {
return;
}
$newToken = $this->tokenManager->ensureValidToken($this->oauthAccount);
if ($newToken !== null) {
$this->accessToken = $newToken;
}
}
public function getConfig()
{
return $this->config;
}
// returns only active (archived=false)
public function getOwners(): array
{
return $this->getNewInstance()->crm()->owners()->getAll();
}
/**
* @param bool $archived
*
* @return array<Owner>|[]
*/
public function getOwnersArchived(bool $archived = true): array
{
$endpoint = '/crm/v3/owners';
$queryParams = [
'archived' => $archived ? 'true' : 'false',
];
$queryString = http_build_query($queryParams);
$owners = [];
try {
$response = $this->makeRequest(endpoint: $endpoint, queryString: $queryString);
$responseData = $response?->toArray();
foreach ($responseData['results'] as $result) {
try {
$owners[] = Owner::create($result);
} catch (Throwable $e) {
$this->log->error('[HubSpot] Failed to process owner data', [
'result' => $result,
'error' => $e->getMessage(),
]);
continue;
}
}
} catch (Throwable $e) {
$this->log->error('HubSpot] Failed to fetch owners', [
'archived' => $archived,
'error' => $e->getMessage(),
]);
return [];
}
return $owners;
}
public function getMeeting(string $engagementId): ObjectWithAssociations
{
return $this->getNewInstance()->crm()->objects()->basicApi()
->getById('meeting', $engagementId, null, 'contact,company,deal');
}
public function deleteEngagement(string $engagementId): void
{
$this->getInstance()->engagements()->delete((int) $engagementId);
}
public function getAssociationsData(array $ids, string $fromObject, string $toObject): array
{
$associationData = [];
$idChunks = array_chunk($ids, self::ASSOCIATIONS_BATCH_SIZE_LIMIT);
foreach ($idChunks as $idChunk) {
try {
$batchInput = new \HubSpot\Client\Crm\Associations\Model\BatchInputPublicObjectId();
$batchInput->setInputs(array_map(function ($id) {
$publicObjectId = new \HubSpot\Client\Crm\Associations\Model\PublicObjectId();
$publicObjectId->setId($id);
return $publicObjectId;
}, $idChunk));
$associatedObjectsData = $this
->getNewInstance()
->crm()
->associations()
->batchApi()
->read($fromObject, $toObject, $batchInput);
if ($associatedObjectsData instanceof \HubSpot\Client\Crm\Associations\Model\BatchResponsePublicAssociationMulti) {
foreach ($associatedObjectsData->getResults() as $association) {
$from = $association->getFrom()->getId();
$toAssociations = $association->getTo();
if (! empty($toAssociations)) {
$associationData[$from] = array_map(function ($item) {
return $item->getId();
}, $toAssociations);
}
}
}
// } catch (RateLimitException $e) {
// throw $e;
} catch (\Exception $e) {
$this->log->error('[Hubspot] Failed to fetch associations', [
'from_object' => $fromObject,
'to_object' => $toObject,
'reason' => $e->getMessage(),
]);
}
}
return $associationData;
}
/**
* @throws \Exception
*/
private function getNoteAssociationType(NoteObject $noteObject): string
{
return match($noteObject) {
NoteObject::Opportunity => 'note_to_deal',
NoteObject::Lead, NoteObject::Contact => 'note_to_contact', // or 'note_to_lead' if your portal supports it
NoteObject::Account => 'note_to_company',
NoteObject::Call, NoteObject::Event => throw new \Exception('Not supported'),
};
}
/**
* @throws \Exception
*/
private function getNoteObject(NoteObject $noteObject): string
{
return match($noteObject) {
NoteObject::Opportunity => 'deal',
NoteObject::Lead, NoteObject::Contact => 'contact',
NoteObject::Account => 'company',
NoteObject::Call, NoteObject::Event => throw new \Exception('Not supported'),
};
}
public function addAssociations(string $objectType, string $associationType, array $payload): Response
{
$endpoint = "/crm/v4/associations/$objectType/$associationType/batch/create";
return $this->makeRequest($endpoint, 'POST', $payload);
}
public function removeAssociations(string $objectType, string $associationType, array $payload): Response
{
$endpoint = "/crm/v4/associations/$objectType/$associationType/batch/archive";
return $this->makeRequest($endpoint, 'POST', $payload);
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
19
Previous Highlighted Error
Next Highlighted Error
[2026-05-07 14:21:15] local.INFO: [Hubspot] DEBUG Getting headers {
"headers":{
"Date":["Thu,07 May 2026 14:21:15 GMT"],
"Content-Type":["application/json;charset=utf-8"],
"Transfer-Encoding":["chunked"],
"Connection":["keep-alive"],
"CF-Ray":["9f80deb8db60dc3a-SOF"],
"CF-Cache-Status":["DYNAMIC"],
"Strict-Transport-Security":["max-age=31536000; includeSubDomains; preload"],
"Vary":["origin,
accept-encoding"],
"access-control-allow-credentials":["false"],
"server-timing":["hcid;desc=\"019e02d0-6fd8-7812-bdba-885b7ccb3ee3\",
cfr;desc=\"9f80deb8e7c6dc3a-IAD\""],
"x-content-type-options":["nosniff"],
"x-hubspot-correlation-id":["019e02d0-6fd8-7812-bdba-885b7ccb3ee3"],
"Set-Cookie":["__cf_bm=SIUrtdQgXVrik50pdqF6hZVYKhzTnQBidvMabeCtm0Y-1778163675-[IP_ADDRESS]-rI.ZggtDKxTge5zr8_2gbBfWMQQ.ufZEXDZyHz2mBUFdzdo2gTHEsOkXMSEShjK0hGYxNhUGM1ZoBpX7BcFZcHEjA7Cs_.SMUhUnd2nYjko; path=/; expires=Thu,
07-May-26 14:51:15 GMT; domain=.hubapi.com; HttpOnly; Secure; SameSite=None"],
"Report-To":["{
\"endpoints\":[{
\"url\":\"https:\\/\\/a.nel.cloudflare.com\\/report\\/v4?s=NYAlsVTP0fYm32qrSDjxYE4sd2RWRqiSp3wHsmdEgZlzoYdxI%2BIxVpHmsKn3O%2BKVA3mFIJ2m7YRECDGSM%2BW2IYTzo6FM4%2BdUIjURO8srzKSvJgZ%2BQ6R79arKQw3uHLlX\"}],
\"group\":\"cf-nel\",
\"max_age\":604800}"],
"NEL":["{
\"success_fraction\":0.01,
\"report_to\":\"cf-nel\",
\"max_age\":604800}"],
"Server":["cloudflare"]}} {
"correlation_id":"95236535-ec98-4541-b92a-adfa73b69eab",
"trace_id":"c7ab8365-903f-46d4-9403-0e5b551e3545"}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
18183
|
NULL
|
NULL
|
NULL
|
|
18186
|
NULL
|
0
|
2026-05-11T11:03:02.631548+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-11/1778 /Users/lukas/.screenpipe/data/data/2026-05-11/1778497382631_m1.jpg...
|
PhpStorm
|
faVsco.js – Client.php
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
JY-20725-handle-HS-search Project: faVsco.js, menu
JY-20725-handle-HS-search-rate-limit, menu
Start Listening for PHP Debug Connections
HandleHubspotRateLimitTest
Run 'HandleHubspotRateLimitTest'
Debug 'HandleHubspotRateLimitTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
2
67
3
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Services\Crm\Hubspot;
use HubSpot\Client\Crm\Deals\ApiException as DealApiException;
use HubSpot\Client\Crm\Contacts\ApiException as ContactApiException;
use HubSpot\Client\Crm\Companies\ApiException as CompanyApiException;
use HubSpot\Client\Crm\Contacts\Model\SimplePublicObjectWithAssociations as ContactsWithAssociations;
use HubSpot\Client\Crm\Companies\Model\SimplePublicObjectWithAssociations as CompaniesWithAssociations;
use HubSpot\Client\Crm\Deals\Model\SimplePublicObjectWithAssociations as DealWithAssociations;
use HubSpot\Client\Crm\Objects\Model\SimplePublicObjectInput;
use HubSpot\Client\Crm\Objects\Model\SimplePublicObjectWithAssociations as ObjectWithAssociations;
use HubSpot\Client\Crm\Pipelines\Model\Error;
use HubSpot\Client\Crm\Pipelines\Model\PipelineStage;
use HubSpot\Client\Crm\Properties\Model\Property;
use HubSpot\Discovery\Discovery;
use Jiminny\Exceptions\CrmException;
use Jiminny\Exceptions\RateLimitException;
use Jiminny\Exceptions\SocialAccountTokenInvalidException;
use Jiminny\Jobs\Crm\NoteObject;
use Jiminny\Models\Crm\Field;
use Jiminny\Services\Crm\BaseClient;
use Jiminny\Services\Crm\Hubspot\DTO\Response\Owner;
use Jiminny\Services\SocialAccountService;
use SevenShores\Hubspot\Exceptions\BadRequest;
use SevenShores\Hubspot\Exceptions\HubspotException;
use SevenShores\Hubspot\Factory;
use SevenShores\Hubspot\Http\Response;
use Jiminny\Services\Crm\Hubspot\Pagination\HubspotPaginationService;
use Illuminate\Support\Facades\Redis;
use Throwable;
/**
* @phpstan-type CrmFieldOption array{id:string, label:string, value?:string}
*/
class Client extends BaseClient implements HubspotClientInterface
{
public const string MIN_API_VERSION = '2';
public const string BASE_URL = '[URL_WITH_CREDENTIALS] T
* @param callable(): T $apiCall
* @return T
*
* @throws RateLimitException
*/
private function executeRequest(callable $apiCall)
{
$cacheKey = $this->getRateLimitCacheKey();
$cachedRetryAfter = Redis::get($cacheKey);
if (is_string($cachedRetryAfter) && is_numeric($cachedRetryAfter)) {
throw new RateLimitException(
'Hubspot rate limit (cached circuit-breaker)',
(int) $cachedRetryAfter,
);
}
try {
return $apiCall();
} catch (Throwable $e) {
if ($this->isHubspotRateLimit($e)) {
$retryAfter = $this->parseRetryAfter($e);
Redis::setex($cacheKey, $retryAfter, (string) $retryAfter);
$this->log->warning('[Hubspot] Received 429 from API', [
'team_id' => $this->config->team_id,
'config_id' => $this->config->getId(),
'retry_after' => $retryAfter,
'message' => $this->parseMessage($e),
'reason' => $e->getMessage(),
]);
throw new RateLimitException('Hubspot returned 429', $retryAfter, $e);
}
throw $e;
}
}
private function getRateLimitCacheKey(): string
{
return sprintf('hubspot:ratelimit:portal:%d', $this->config->getId());
}
public function isHubspotRateLimit(Throwable $e): bool
{
if ($e instanceof BadRequest
|| $e instanceof DealApiException
|| $e instanceof ContactApiException
|| $e instanceof CompanyApiException
|| $e instanceof \GuzzleHttp\Exception\RequestException
) {
return (int) $e->getCode() === 429;
}
return false;
}
public function parseRetryAfter(Throwable $e): int
{
if (method_exists($e, 'getResponseHeaders')) {
$headers = $e->getResponseHeaders() ?: [];
$value = $headers['Retry-After'] ?? $headers['retry-after'] ?? null;
if (is_array($value)) {
$value = $value[0] ?? null;
}
if (is_numeric($value)) {
return (int) $value;
}
}
$message = strtolower($this->parseMessage($e));
if (str_contains($message, 'daily')) {
return 600;
}
if (str_contains($message, 'ten secondly')) {
return 10;
}
if (str_contains($message, 'secondly')) {
return 1;
}
$this->log->warning('[Hubspot] No retry-after header or known message, using default', [
'exception_class' => get_class($e),
'message' => $message,
]);
return 10;
}
public function parseMessage(Throwable $e): string
{
if (! method_exists($e, 'getResponseBody')) {
return '';
}
$body = $e->getResponseBody();
if (is_string($body)) {
$body = json_decode($body, true) ?? [];
}
if (! is_array($body)) {
return '';
}
return (string) ($body['message'] ?? '');
}
public function getMinimumApiVersion(): string
{
return self::MIN_API_VERSION;
}
public function getInstance(): Factory
{
return new Factory([
'key' => $this->accessToken,
'oauth2' => true,
'base_url' => $this->baseUrl,
]);
}
public function getNewInstance(): Discovery
{
return \HubSpot\Factory::createWithAccessToken($this->accessToken);
}
/**
* Secondly and daily limits for Hubspot API
*
* Product Tier: Free & Starter | Professional & Enterprise | API add-on (any tier)
* Burst: 100/10 seconds | 150/10 seconds | 200/10 seconds
* Daily: 250,000 | 500,000 | 1,000,000
*
* Official documentation states: The search endpoints are rate limited to five requests per second.
* Since with 5 RPS were still hitting secondly rate limits we lowered it to 4
*/
public function getPaginatedData(array $payload, string $type, int $offset = 0): array
{
$total = 0;
$lastId = null;
$rows = [];
foreach ($this->getPaginatedDataGenerator($payload, $type, $offset, $total, $lastId) as $row) {
$rows[] = $row;
}
return ['results' => $rows, 'total' => $total, 'last_record' => $lastId];
}
/**
* @throws HubspotException
* @throws SocialAccountTokenInvalidException
* @throws BadRequest
*/
public function getPaginatedDataGenerator(
array $payload,
string $type,
int $offset = 0,
int &$total = 0,
?string &$lastRecordId = null
): \Generator {
return $this->paginationService->getPaginatedDataGenerator(
$this,
$payload,
$type,
$offset,
$total,
$lastRecordId
);
}
/**
* Execute a search request against HubSpot CRM objects with rate limiting.
*
* @param string $objectType The object type ('deals', 'companies', 'contacts', 'calls')
* @param array<string, mixed> $payload The search payload with filters, sorts, properties, etc.
* @return array The search response with 'results', 'total', 'paging' keys
* @throws RateLimitException When rate limit is hit
* @throws HubspotException On API errors
*/
public function search(string $objectType, array $payload): array
{
$endpoint = self::BASE_URL . "/crm/v3/objects/{$objectType}/search";
return $this->executeRequest(function () use ($endpoint, $payload) {
$response = $this->getInstance()->getClient()->request('POST', $endpoint, ['json' => $payload]);
return $response->toArray();
});
}
/**
* @throws DealApiException
* @throws CrmException
*/
public function getOpportunityById(string $crmId, array $fields): array
{
try {
// $deal = $this->executeRequest(fn () => $this->getNewInstance()->crm()->deals()->basicApi()->getById(
$deal = $this->getNewInstance()->crm()->deals()->basicApi()->getById(
$crmId,
implode(',', $fields),
'companies,contacts'
);
} catch (DealApiException $e) {
$this->log->info('[Hubspot] Failed to fetch opportunity', [
'crm_id' => $crmId,
'reason' => $e->getMessage(),
]);
throw $e;
}
if (! $deal instanceof DealWithAssociations) {
throw new CrmException('Deal not found');
}
return [
'id' => $deal->getId(),
'properties' => $deal->getProperties(),
'associations' => $deal->getAssociations(),
];
}
/**
* Generic batch read method for HubSpot objects
*
* @param string $objectType The object type ('deals', 'companies', 'contacts')
* @param array<string> $crmIds Array of HubSpot object IDs (max 100)
* @param array<string> $fields Array of property names to fetch
*
* @return array<string, array> Array keyed by CRM ID with object data
*/
private function batchReadObjects(string $objectType, array $crmIds, array $fields): array
{
if (empty($crmIds)) {
return [];
}
$this->validateBatchSize($objectType, $crmIds);
$this->ensureValidToken();
try {
$batchConfig = $this->createBatchConfiguration($objectType);
$batchReadRequest = $this->prepareBatchRequest($batchConfig, $crmIds, $fields);
$response = $batchConfig['api']->read($batchReadRequest);
$this->validateApiResponse($response, $objectType);
$results = $this->processApiResults($response);
$this->logBatchResults($objectType, $crmIds, $results);
return $results;
} catch (\Throwable $e) {
$this->handleBatchError($e, $objectType, $crmIds);
}
}
private function validateBatchSize(string $objectType, array $crmIds): void
{
if (count($crmIds) > 100) {
throw new \InvalidArgumentException("Batch size cannot exceed 100 {$objectType}");
}
}
private function createBatchConfiguration(string $objectType): array
{
$configurations = [
'deals' => [
'batchReadRequest' => new \HubSpot\Client\Crm\Deals\Model\BatchReadInputSimplePublicObjectId(),
'inputClass' => \HubSpot\Client\Crm\Deals\Model\SimplePublicObjectId::class,
'api' => $this->getNewInstance()->crm()->deals()->batchApi(),
],
'companies' => [
'batchReadRequest' => new \HubSpot\Client\Crm\Companies\Model\BatchReadInputSimplePublicObjectId(),
'inputClass' => \HubSpot\Client\Crm\Companies\Model\SimplePublicObjectId::class,
'api' => $this->getNewInstance()->crm()->companies()->batchApi(),
],
'contacts' => [
'batchReadRequest' => new \HubSpot\Client\Crm\Contacts\Model\BatchReadInputSimplePublicObjectId(),
'inputClass' => \HubSpot\Client\Crm\Contacts\Model\SimplePublicObjectId::class,
'api' => $this->getNewInstance()->crm()->contacts()->batchApi(),
],
];
if (! isset($configurations[$objectType])) {
throw new \InvalidArgumentException("Unsupported object type: {$objectType}");
}
return $configurations[$objectType];
}
private function prepareBatchRequest(array $batchConfig, array $crmIds, array $fields): object
{
$batchReadRequest = $batchConfig['batchReadRequest'];
$inputClass = $batchConfig['inputClass'];
$inputs = array_map(function ($crmId) use ($inputClass) {
$input = new $inputClass();
$input->setId($crmId);
return $input;
}, $crmIds);
$batchReadRequest->setInputs($inputs);
$batchReadRequest->setProperties($fields);
return $batchReadRequest;
}
private function validateApiResponse($response, string $objectType): void
{
if (! $response) {
throw new CrmException("HubSpot API returned null response for {$objectType} batch read");
}
}
private function processApiResults($response): array
{
$results = [];
$responseResults = $response->getResults();
if ($responseResults) {
foreach ($responseResults as $object) {
if ($object && $object->getId()) {
$results[$object->getId()] = [
'id' => $object->getId(),
'properties' => $object->getProperties() ?: [],
];
}
}
}
return $results;
}
private function logBatchResults(string $objectType, array $crmIds, array $results): void
{
$this->log->info("[HubSpot] Batch fetched {$objectType}", [
'requested_count' => count($crmIds),
'returned_count' => count($results),
'crm_ids' => $crmIds,
]);
}
private function handleBatchError(\Throwable $e, string $objectType, array $crmIds): void
{
$errorMessage = $e->getMessage() ?: 'Unknown error';
$errorTrace = $e->getTraceAsString() ?: 'No trace available';
$this->log->error("[HubSpot] Failed to batch fetch {$objectType}", [
'crm_ids' => $crmIds,
'error' => $errorMessage,
'trace' => $errorTrace,
]);
throw new CrmException("Failed to batch fetch {$objectType}: " . $errorMessage);
}
/**
* Batch read multiple opportunities by their CRM IDs
*
* @param array<string> $crmIds Array of HubSpot deal IDs (max 100)
* @param array<string> $fields Array of property names to fetch
*
* @return array<string, array> Array keyed by CRM ID with opportunity data
*/
public function getOpportunitiesByIds(array $crmIds, array $fields): array
{
return $this->batchReadObjects('deals', $crmIds, $fields);
}
/**
* Batch read multiple companies by their CRM IDs
*
* @param array<string> $crmIds Array of HubSpot company IDs (max 100)
* @param array<string> $fields Array of property names to fetch
*
* @return array<string, array> Array keyed by CRM ID with company data
*/
public function getCompaniesByIds(array $crmIds, array $fields): array
{
return $this->batchReadObjects('companies', $crmIds, $fields);
}
/**
* Batch read multiple contacts by their CRM IDs
*
* @param array<string> $crmIds Array of HubSpot contact IDs (max 100)
* @param array<string> $fields Array of property names to fetch
*
* @return array<string, array> Array keyed by CRM ID with contact data
*/
public function getContactsByIds(array $crmIds, array $fields): array
{
return $this->batchReadObjects('contacts', $crmIds, $fields);
}
/**
* @throws CompanyApiException
* @throws CrmException
*/
public function getAccountById(string $crmId, array $fields): array
{
try {
$company = $this->getNewInstance()->crm()->companies()->basicApi()->getById(
$crmId,
implode(',', $fields),
);
} catch (CompanyApiException $e) {
$this->log->info('[Hubspot] Failed to fetch account', [
'crm_id' => $crmId,
'reason' => $e->getMessage(),
]);
throw $e;
}
if (! $company instanceof CompaniesWithAssociations) {
throw new CrmException('Account not found');
}
return [
'id' => $company->getId(),
'properties' => $company->getProperties(),
];
}
/**
* @throws ContactApiException
* @throws CrmException
*/
public function getContactById(string $crmId, array $fields): array
{
try {
$contact = $this->getNewInstance()->crm()->contacts()->basicApi()->getById(
$crmId,
implode(',', $fields)
);
} catch (ContactApiException $e) {
$this->log->info('[Hubspot] Failed to fetch contact', [
'crm_id' => $crmId,
'reason' => $e->getMessage(),
]);
throw $e;
}
if (! $contact instanceof ContactsWithAssociations) {
throw new CrmException('Contact not found');
}
return [
'id' => $contact->getId(),
'properties' => $contact->getProperties(),
];
}
/**
* This is email search request that Hubspot offers as GET (more generous quota)
*/
public function getContactByEmail(string $email, array $fields = []): array
{
try {
$contact = $this->getNewInstance()->crm()->contacts()->basicApi()->getById(
$email,
implode(',', $fields),
null,
false,
'email'
);
return [
'id' => $contact->getId(),
'properties' => $contact->getProperties(),
];
} catch (ContactApiException $e) {
$this->log->info('[Hubspot] Failed to fetch contact', [
'email' => $email,
'reason' => $e->getMessage(),
]);
return [];
}
}
/**
* @throws CrmException
*/
public function fetchProperty(string $objectType, string $propertyId): Property
{
$result = $this->getNewInstance()->crm()->properties()->coreApi()->getByName($objectType, $propertyId);
if (! $result instanceof Property) {
$this->log->error('[Hubspot] Failed to fetch property', [
'object_type' => $objectType,
'property_id' => $propertyId,
'reason' => $result->getMessage(),
]);
throw new CrmException('Failed to fetch property');
}
return $result;
}
/**
* @return array<CrmFieldOption>
*/
public function fetchPropertyOptions(string $objectType, string $propertyId): array
{
/** @var array<CrmFieldOption> */
return $this->fetchProperty($objectType, $propertyId)->getOptions();
}
/**
* @return array<array{id:string, label:string, deleted:bool}>
*/
public function fetchCallDispositions(): array
{
/** @var Response $response */
$response = $this->getInstance()->engagements()->getCallDispositions();
/**
* @var array<array{
* id:string,
* label:string,
* deleted: bool
* }>
*/
return $response->toArray();
}
/**
* @return array<CrmFieldOption>
*/
public function fetchOpportunityPipelineStages(): array
{
$stages = [];
$apiResponse = $this->getNewInstance()->crm()->pipelines()->pipelinesApi()->getAll('deals');
if ($apiResponse instanceof Error) {
$this->log->error('[Hubspot] Failed to fetch opportunity pipelines', [
'reason' => $apiResponse->getMessage(),
]);
return [];
}
foreach ($apiResponse->getResults() as $pipeline) {
$pipelineStages = array_map(
static function (PipelineStage $stage) {
return [
'id' => $stage->getId(),
'label' => $stage->getLabel(),
];
},
$pipeline->getStages()
);
$stages = array_merge($stages, $pipelineStages);
}
return $stages;
}
public function fetchOpportunityPipelines(): array
{
$pipelines = [];
try {
$apiResponse = $this->makeRequest('/crm/v3/pipelines/deals');
} catch (\Exception $e) {
$this->log->info('[Hubspot] Failed to fetch opportunity pipelines', [
'reason' => $e->getMessage(),
]);
return [];
}
$response = $apiResponse->toArray();
foreach ($response['results'] as $pipeline) {
$pipelines[] = [
'id' => $pipeline['id'],
'label' => $pipeline['label'],
];
}
return $pipelines;
}
/**
* @return array<CrmFieldOption>
*/
public function fetchMeetingOutcomeFieldOptions(Field $field): array
{
return $field->getCrmProviderId() === 'meetingOutcome'
? $this->fetchMeetingOutcomeTypes()
: $this->fetchCallActivityTypes();
}
public function fetchMeetingOutcomeTypes(): array
{
return $this->extractMeetingTypeOptions(
'[URL_WITH_CREDENTIALS] Response $response */
$response = $this->getInstance()
->getClient()
->request('GET', $endpoint);
/**
* @var array<array{
* value: string,
* label: string,
* displayOrder: int
* }> $optionData
*/
$optionData = $response->toArray()['options'] ?? [];
$options = [];
foreach ($optionData as $item) {
$options[] = [
'id' => $item['value'],
'value' => $item['value'],
'label' => $item['label'],
'display_order' => $item['displayOrder'],
];
}
return $options;
}
/**
* @return array<CrmFieldOption>
*/
public function fetchDispositionFieldOptions(): array
{
$options = [];
$dispositions = $this->fetchCallDispositions();
foreach ($dispositions as $disposition) {
if ($disposition['deleted'] !== false) {
continue;
}
$option['value'] = $disposition['id'];
$option['id'] = $disposition['id'];
$option['label'] = $disposition['label'];
$options[] = $option;
}
return $options;
}
/**
* @return array<CrmFieldOption>
*/
public function fetchOpportunityFieldOptions(Field $field): array
{
if ($field->isStageField()) {
return $this->fetchOpportunityPipelineStages();
}
if ($field->isPipelineField()) {
return $this->fetchOpportunityPipelines();
}
return $this->fetchPropertyOptions('deals', $field->getCrmProviderId());
}
/**
* @throws BadRequest
* @throws HubspotException
*/
public function makeRequest(string $endpoint, $method = 'GET', $payload = [], ?string $queryString = null)
{
$endpoint = self::BASE_URL . $endpoint;
if ($method === 'GET') {
return $this->getInstance()->getClient()?->request(
method: $method,
endpoint: $endpoint,
query_string: $queryString
);
} else {
return $this->getInstance()->getClient()->request($method, $endpoint, [
'json' => ($payload),
]);
}
}
/**
* @throws BadRequest
* @throws HubspotException
*/
public function createMeeting(array $payload): Response
{
$endpoint = '/crm/v3/objects/meetings';
return $this->makeRequest($endpoint, 'POST', $payload);
}
/**
* @throws BadRequest
* @throws HubspotException
*/
public function updateMeeting(string $meetingId, array $payload): Response
{
$endpoint = '/crm/v3/objects/meetings/' . $meetingId;
return $this->makeRequest($endpoint, 'PATCH', $payload);
}
/**
* @throws \Exception
*/
public function createNote(
string $body,
string $ownerId,
int $timestamp,
string $objectId,
NoteObject $noteObject
): ?string {
try {
$noteInput = new SimplePublicObjectInput([
'properties' => [
'hs_note_body' => $body,
'hubspot_owner_id' => $ownerId,
'hs_timestamp' => $timestamp,
],
]);
// Create note
$note = $this->getNewInstance()->crm()->objects()->basicApi()->create('note', $noteInput);
$this->getNewInstance()->crm()->objects()->associationsApi()->create(
'note',
$note->getId(),
$this->getNoteObject($noteObject),
$objectId,
$this->getNoteAssociationType($noteObject),
);
return $note->getId();
} catch (\Exception $e) {
$this->log->error('[Hubspot] Failed to create note', [
'objectId' => $objectId,
'noteObject' => $noteObject->getObjectType(),
'reason' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
return null;
}
public function updateEngagement(string $objectId, array $engagement, array $metadata): void
{
$this->getInstance()->engagements()->update($objectId, $engagement, $metadata);
}
public function getEngagementData(string $engagementId): array
{
$engagement = $this->getInstance()->engagements()->get($engagementId);
return $engagement->toArray();
}
public function createEngagement(array $engagement, array $associations, array $metadata): Response
{
return $this->getInstance()
->engagements()
->create($engagement, $associations, $metadata);
}
public function isUnauthorizedException(\Exception $e): bool
{
// Check for specific HubSpot API exception types first
if ($e instanceof BadRequest) {
// BadRequest can contain 401 status codes
return $e->getCode() === 401;
}
// Check for HTTP client exceptions with status codes
if ($e instanceof \GuzzleHttp\Exception\RequestException && $e->hasResponse()) {
$response = $e->getResponse();
if ($response !== null) {
return $response->getStatusCode() === 401;
}
}
// Check for Guzzle HTTP exceptions
if ($e instanceof \GuzzleHttp\Exception\ClientException) {
return $e->getCode() === 401;
}
// Fallback to string matching as last resort, but be more specific
$message = strtolower($e->getMessage());
return str_contains($message, '401 unauthorized') ||
str_contains($message, 'http 401') ||
str_contains($message, 'status code 401') ||
(preg_match('/\b401\b/', $message) && str_contains($message, 'unauthorized'));
}
/**
* Validates and refreshes the access token if needed before API requests.
* This ensures long-running processes don't fail due to token expiration.
*
* @throws SocialAccountTokenInvalidException
*/
public function ensureValidToken(): void
{
if ($this->oauthAccount === null) {
return;
}
$newToken = $this->tokenManager->ensureValidToken($this->oauthAccount);
if ($newToken !== null) {
$this->accessToken = $newToken;
}
}
public function getConfig()
{
return $this->config;
}
// returns only active (archived=false)
public function getOwners(): array
{
return $this->getNewInstance()->crm()->owners()->getAll();
}
/**
* @param bool $archived
*
* @return array<Owner>|[]
*/
public function getOwnersArchived(bool $archived = true): array
{
$endpoint = '/crm/v3/owners';
$queryParams = [
'archived' => $archived ? 'true' : 'false',
];
$queryString = http_build_query($queryParams);
$owners = [];
try {
$response = $this->makeRequest(endpoint: $endpoint, queryString: $queryString);
$responseData = $response?->toArray();
foreach ($responseData['results'] as $result) {
try {
$owners[] = Owner::create($result);
} catch (Throwable $e) {
$this->log->error('[HubSpot] Failed to process owner data', [
'result' => $result,
'error' => $e->getMessage(),
]);
continue;
}
}
} catch (Throwable $e) {
$this->log->error('HubSpot] Failed to fetch owners', [
'archived' => $archived,
'error' => $e->getMessage(),
]);
return [];
}
return $owners;
}
public function getMeeting(string $engagementId): ObjectWithAssociations
{
return $this->getNewInstance()->crm()->objects()->basicApi()
->getById('meeting', $engagementId, null, 'contact,company,deal');
}
public function deleteEngagement(string $engagementId): void
{
$this->getInstance()->engagements()->delete((int) $engagementId);
}
public function getAssociationsData(array $ids, string $fromObject, string $toObject): array
{
$associationData = [];
$idChunks = array_chunk($ids, self::ASSOCIATIONS_BATCH_SIZE_LIMIT);
foreach ($idChunks as $idChunk) {
try {
$batchInput = new \HubSpot\Client\Crm\Associations\Model\BatchInputPublicObjectId();
$batchInput->setInputs(array_map(function ($id) {
$publicObjectId = new \HubSpot\Client\Crm\Associations\Model\PublicObjectId();
$publicObjectId->setId($id);
return $publicObjectId;
}, $idChunk));
$associatedObjectsData = $this
->getNewInstance()
->crm()
->associations()
->batchApi()
->read($fromObject, $toObject, $batchInput);
if ($associatedObjectsData instanceof \HubSpot\Client\Crm\Associations\Model\BatchResponsePublicAssociationMulti) {
foreach ($associatedObjectsData->getResults() as $association) {
$from = $association->getFrom()->getId();
$toAssociations = $association->getTo();
if (! empty($toAssociations)) {
$associationData[$from] = array_map(function ($item) {
return $item->getId();
}, $toAssociations);
}
}
}
// } catch (RateLimitException $e) {
// throw $e;
} catch (\Exception $e) {
$this->log->error('[Hubspot] Failed to fetch associations', [
'from_object' => $fromObject,
'to_object' => $toObject,
'reason' => $e->getMessage(),
]);
}
}
return $associationData;
}
/**
* @throws \Exception
*/
private function getNoteAssociationType(NoteObject $noteObject): string
{
return match($noteObject) {
NoteObject::Opportunity => 'note_to_deal',
NoteObject::Lead, NoteObject::Contact => 'note_to_contact', // or 'note_to_lead' if your portal supports it
NoteObject::Account => 'note_to_company',
NoteObject::Call, NoteObject::Event => throw new \Exception('Not supported'),
};
}
/**
* @throws \Exception
*/
private function getNoteObject(NoteObject $noteObject): string
{
return match($noteObject) {
NoteObject::Opportunity => 'deal',
NoteObject::Lead, NoteObject::Contact => 'contact',
NoteObject::Account => 'company',
NoteObject::Call, NoteObject::Event => throw new \Exception('Not supported'),
};
}
public function addAssociations(string $objectType, string $associationType, array $payload): Response
{
$endpoint = "/crm/v4/associations/$objectType/$associationType/batch/create";
return $this->makeRequest($endpoint, 'POST', $payload);
}
public function removeAssociations(string $objectType, string $associationType, array $payload): Response
{
$endpoint = "/crm/v4/associations/$objectType/$associationType/batch/archive";
return $this->makeRequest($endpoint, 'POST', $payload);
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
19
Previous Highlighted Error
Next Highlighted Error
[2026-05-07 14:21:15] local.INFO: [Hubspot] DEBUG Getting headers {
"headers":{
"Date":["Thu,07 May 2026 14:21:15 GMT"],
"Content-Type":["application/json;charset=utf-8"],
"Transfer-Encoding":["chunked"],
"Connection":["keep-alive"],
"CF-Ray":["9f80deb8db60dc3a-SOF"],
"CF-Cache-Status":["DYNAMIC"],
"Strict-Transport-Security":["max-age=31536000; includeSubDomains; preload"],
"Vary":["origin,
accept-encoding"],
"access-control-allow-credentials":["false"],
"server-timing":["hcid;desc=\"019e02d0-6fd8-7812-bdba-885b7ccb3ee3\",
cfr;desc=\"9f80deb8e7c6dc3a-IAD\""],
"x-content-type-options":["nosniff"],
"x-hubspot-correlation-id":["019e02d0-6fd8-7812-bdba-885b7ccb3ee3"],
"Set-Cookie":["__cf_bm=SIUrtdQgXVrik50pdqF6hZVYKhzTnQBidvMabeCtm0Y-1778163675-[IP_ADDRESS]-rI.ZggtDKxTge5zr8_2gbBfWMQQ.ufZEXDZyHz2mBUFdzdo2gTHEsOkXMSEShjK0hGYxNhUGM1ZoBpX7BcFZcHEjA7Cs_.SMUhUnd2nYjko; path=/; expires=Thu,
07-May-26 14:51:15 GMT; domain=.hubapi.com; HttpOnly; Secure; SameSite=None"],
"Report-To":["{
\"endpoints\":[{
\"url\":\"https:\\/\\/a.nel.cloudflare.com\\/report\\/v4?s=NYAlsVTP0fYm32qrSDjxYE4sd2RWRqiSp3wHsmdEgZlzoYdxI%2BIxVpHmsKn3O%2BKVA3mFIJ2m7YRECDGSM%2BW2IYTzo6FM4%2BdUIjURO8srzKSvJgZ%2BQ6R79arKQw3uHLlX\"}],
\"group\":\"cf-nel\",
\"max_age\":604800}"],
"NEL":["{
\"success_fraction\":0.01,
\"report_to\":\"cf-nel\",
\"max_age\":604800}"],
"Server":["cloudflare"]}} {
"correlation_id":"95236535-ec98-4541-b92a-adfa73b69eab",
"trace_id":"c7ab8365-903f-46d4-9403-0e5b551e3545"}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JY-20725-handle-HS-search-rate-limit, menu","depth":5,"on_screen":true,"help_text":"Git Branch: JY-20725-handle-HS-search-rate-limit","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"HandleHubspotRateLimitTest","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'HandleHubspotRateLimitTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'HandleHubspotRateLimitTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"2","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"67","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"3","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Services\\Crm\\Hubspot;\n\nuse HubSpot\\Client\\Crm\\Deals\\ApiException as DealApiException;\nuse HubSpot\\Client\\Crm\\Contacts\\ApiException as ContactApiException;\nuse HubSpot\\Client\\Crm\\Companies\\ApiException as CompanyApiException;\nuse HubSpot\\Client\\Crm\\Contacts\\Model\\SimplePublicObjectWithAssociations as ContactsWithAssociations;\nuse HubSpot\\Client\\Crm\\Companies\\Model\\SimplePublicObjectWithAssociations as CompaniesWithAssociations;\nuse HubSpot\\Client\\Crm\\Deals\\Model\\SimplePublicObjectWithAssociations as DealWithAssociations;\nuse HubSpot\\Client\\Crm\\Objects\\Model\\SimplePublicObjectInput;\nuse HubSpot\\Client\\Crm\\Objects\\Model\\SimplePublicObjectWithAssociations as ObjectWithAssociations;\nuse HubSpot\\Client\\Crm\\Pipelines\\Model\\Error;\nuse HubSpot\\Client\\Crm\\Pipelines\\Model\\PipelineStage;\nuse HubSpot\\Client\\Crm\\Properties\\Model\\Property;\nuse HubSpot\\Discovery\\Discovery;\nuse Jiminny\\Exceptions\\CrmException;\nuse Jiminny\\Exceptions\\RateLimitException;\nuse Jiminny\\Exceptions\\SocialAccountTokenInvalidException;\nuse Jiminny\\Jobs\\Crm\\NoteObject;\nuse Jiminny\\Models\\Crm\\Field;\nuse Jiminny\\Services\\Crm\\BaseClient;\nuse Jiminny\\Services\\Crm\\Hubspot\\DTO\\Response\\Owner;\nuse Jiminny\\Services\\SocialAccountService;\nuse SevenShores\\Hubspot\\Exceptions\\BadRequest;\nuse SevenShores\\Hubspot\\Exceptions\\HubspotException;\nuse SevenShores\\Hubspot\\Factory;\nuse SevenShores\\Hubspot\\Http\\Response;\nuse Jiminny\\Services\\Crm\\Hubspot\\Pagination\\HubspotPaginationService;\nuse Illuminate\\Support\\Facades\\Redis;\nuse Throwable;\n\n/**\n * @phpstan-type CrmFieldOption array{id:string, label:string, value?:string}\n */\nclass Client extends BaseClient implements HubspotClientInterface\n{\n public const string MIN_API_VERSION = '2';\n\n public const string BASE_URL = 'https://api.hubapi.com';\n\n public const int ASSOCIATIONS_BATCH_SIZE_LIMIT = 1000;\n\n private HubspotPaginationService $paginationService;\n private HubspotTokenManager $tokenManager;\n\n public function __construct(\n SocialAccountService $socialAccountService,\n HubspotPaginationService $paginationService,\n HubspotTokenManager $tokenManager\n ) {\n parent::__construct($socialAccountService);\n $this->paginationService = $paginationService;\n $this->tokenManager = $tokenManager;\n\n $this->setBaseUrl(self::BASE_URL);\n $this->setVersion(self::MIN_API_VERSION);\n }\n\n /**\n * Reacts to a rate limits (429) from HubSpot by translating it\n * into a RateLimitException carrying retry_after.\n *\n * Wrap any outbound HubSpot call (SDK or raw HTTP) like:\n *\n * $this->executeRequest(fn () => $this->getNewInstance()->crm()->...);\n *\n * @template T\n * @param callable(): T $apiCall\n * @return T\n *\n * @throws RateLimitException\n */\n private function executeRequest(callable $apiCall)\n {\n $cacheKey = $this->getRateLimitCacheKey();\n\n $cachedRetryAfter = Redis::get($cacheKey);\n if (is_string($cachedRetryAfter) && is_numeric($cachedRetryAfter)) {\n throw new RateLimitException(\n 'Hubspot rate limit (cached circuit-breaker)',\n (int) $cachedRetryAfter,\n );\n }\n\n try {\n return $apiCall();\n } catch (Throwable $e) {\n if ($this->isHubspotRateLimit($e)) {\n $retryAfter = $this->parseRetryAfter($e);\n\n Redis::setex($cacheKey, $retryAfter, (string) $retryAfter);\n\n $this->log->warning('[Hubspot] Received 429 from API', [\n 'team_id' => $this->config->team_id,\n 'config_id' => $this->config->getId(),\n 'retry_after' => $retryAfter,\n 'message' => $this->parseMessage($e),\n 'reason' => $e->getMessage(),\n ]);\n\n throw new RateLimitException('Hubspot returned 429', $retryAfter, $e);\n }\n\n throw $e;\n }\n }\n\n private function getRateLimitCacheKey(): string\n {\n return sprintf('hubspot:ratelimit:portal:%d', $this->config->getId());\n }\n\n public function isHubspotRateLimit(Throwable $e): bool\n {\n if ($e instanceof BadRequest\n || $e instanceof DealApiException\n || $e instanceof ContactApiException\n || $e instanceof CompanyApiException\n || $e instanceof \\GuzzleHttp\\Exception\\RequestException\n ) {\n return (int) $e->getCode() === 429;\n }\n\n return false;\n }\n\n public function parseRetryAfter(Throwable $e): int\n {\n if (method_exists($e, 'getResponseHeaders')) {\n $headers = $e->getResponseHeaders() ?: [];\n $value = $headers['Retry-After'] ?? $headers['retry-after'] ?? null;\n if (is_array($value)) {\n $value = $value[0] ?? null;\n }\n if (is_numeric($value)) {\n return (int) $value;\n }\n }\n\n $message = strtolower($this->parseMessage($e));\n\n if (str_contains($message, 'daily')) {\n return 600;\n }\n if (str_contains($message, 'ten secondly')) {\n return 10;\n }\n if (str_contains($message, 'secondly')) {\n return 1;\n }\n\n $this->log->warning('[Hubspot] No retry-after header or known message, using default', [\n 'exception_class' => get_class($e),\n 'message' => $message,\n ]);\n\n return 10;\n }\n\n public function parseMessage(Throwable $e): string\n {\n if (! method_exists($e, 'getResponseBody')) {\n return '';\n }\n\n $body = $e->getResponseBody();\n if (is_string($body)) {\n $body = json_decode($body, true) ?? [];\n }\n\n if (! is_array($body)) {\n return '';\n }\n\n return (string) ($body['message'] ?? '');\n }\n\n public function getMinimumApiVersion(): string\n {\n return self::MIN_API_VERSION;\n }\n\n public function getInstance(): Factory\n {\n return new Factory([\n 'key' => $this->accessToken,\n 'oauth2' => true,\n 'base_url' => $this->baseUrl,\n ]);\n }\n\n public function getNewInstance(): Discovery\n {\n return \\HubSpot\\Factory::createWithAccessToken($this->accessToken);\n }\n\n /**\n * Secondly and daily limits for Hubspot API\n *\n * Product Tier: Free & Starter | Professional & Enterprise | API add-on (any tier)\n * Burst: 100/10 seconds | 150/10 seconds | 200/10 seconds\n * Daily: 250,000 | 500,000 | 1,000,000\n *\n * Official documentation states: The search endpoints are rate limited to five requests per second.\n * Since with 5 RPS were still hitting secondly rate limits we lowered it to 4\n */\n public function getPaginatedData(array $payload, string $type, int $offset = 0): array\n {\n $total = 0;\n $lastId = null;\n $rows = [];\n foreach ($this->getPaginatedDataGenerator($payload, $type, $offset, $total, $lastId) as $row) {\n $rows[] = $row;\n }\n\n return ['results' => $rows, 'total' => $total, 'last_record' => $lastId];\n }\n\n /**\n * @throws HubspotException\n * @throws SocialAccountTokenInvalidException\n * @throws BadRequest\n */\n public function getPaginatedDataGenerator(\n array $payload,\n string $type,\n int $offset = 0,\n int &$total = 0,\n ?string &$lastRecordId = null\n ): \\Generator {\n return $this->paginationService->getPaginatedDataGenerator(\n $this,\n $payload,\n $type,\n $offset,\n $total,\n $lastRecordId\n );\n }\n\n /**\n * Execute a search request against HubSpot CRM objects with rate limiting.\n *\n * @param string $objectType The object type ('deals', 'companies', 'contacts', 'calls')\n * @param array<string, mixed> $payload The search payload with filters, sorts, properties, etc.\n * @return array The search response with 'results', 'total', 'paging' keys\n * @throws RateLimitException When rate limit is hit\n * @throws HubspotException On API errors\n */\n public function search(string $objectType, array $payload): array\n {\n $endpoint = self::BASE_URL . \"/crm/v3/objects/{$objectType}/search\";\n\n return $this->executeRequest(function () use ($endpoint, $payload) {\n $response = $this->getInstance()->getClient()->request('POST', $endpoint, ['json' => $payload]);\n\n return $response->toArray();\n });\n }\n\n /**\n * @throws DealApiException\n * @throws CrmException\n */\n public function getOpportunityById(string $crmId, array $fields): array\n {\n try {\n// $deal = $this->executeRequest(fn () => $this->getNewInstance()->crm()->deals()->basicApi()->getById(\n $deal = $this->getNewInstance()->crm()->deals()->basicApi()->getById(\n $crmId,\n implode(',', $fields),\n 'companies,contacts'\n );\n } catch (DealApiException $e) {\n $this->log->info('[Hubspot] Failed to fetch opportunity', [\n 'crm_id' => $crmId,\n 'reason' => $e->getMessage(),\n ]);\n\n throw $e;\n }\n\n if (! $deal instanceof DealWithAssociations) {\n throw new CrmException('Deal not found');\n }\n\n return [\n 'id' => $deal->getId(),\n 'properties' => $deal->getProperties(),\n 'associations' => $deal->getAssociations(),\n ];\n }\n\n /**\n * Generic batch read method for HubSpot objects\n *\n * @param string $objectType The object type ('deals', 'companies', 'contacts')\n * @param array<string> $crmIds Array of HubSpot object IDs (max 100)\n * @param array<string> $fields Array of property names to fetch\n *\n * @return array<string, array> Array keyed by CRM ID with object data\n */\n private function batchReadObjects(string $objectType, array $crmIds, array $fields): array\n {\n if (empty($crmIds)) {\n return [];\n }\n\n $this->validateBatchSize($objectType, $crmIds);\n $this->ensureValidToken();\n\n try {\n $batchConfig = $this->createBatchConfiguration($objectType);\n $batchReadRequest = $this->prepareBatchRequest($batchConfig, $crmIds, $fields);\n $response = $batchConfig['api']->read($batchReadRequest);\n\n $this->validateApiResponse($response, $objectType);\n\n $results = $this->processApiResults($response);\n $this->logBatchResults($objectType, $crmIds, $results);\n\n return $results;\n } catch (\\Throwable $e) {\n $this->handleBatchError($e, $objectType, $crmIds);\n }\n }\n\n private function validateBatchSize(string $objectType, array $crmIds): void\n {\n if (count($crmIds) > 100) {\n throw new \\InvalidArgumentException(\"Batch size cannot exceed 100 {$objectType}\");\n }\n }\n\n private function createBatchConfiguration(string $objectType): array\n {\n $configurations = [\n 'deals' => [\n 'batchReadRequest' => new \\HubSpot\\Client\\Crm\\Deals\\Model\\BatchReadInputSimplePublicObjectId(),\n 'inputClass' => \\HubSpot\\Client\\Crm\\Deals\\Model\\SimplePublicObjectId::class,\n 'api' => $this->getNewInstance()->crm()->deals()->batchApi(),\n ],\n 'companies' => [\n 'batchReadRequest' => new \\HubSpot\\Client\\Crm\\Companies\\Model\\BatchReadInputSimplePublicObjectId(),\n 'inputClass' => \\HubSpot\\Client\\Crm\\Companies\\Model\\SimplePublicObjectId::class,\n 'api' => $this->getNewInstance()->crm()->companies()->batchApi(),\n ],\n 'contacts' => [\n 'batchReadRequest' => new \\HubSpot\\Client\\Crm\\Contacts\\Model\\BatchReadInputSimplePublicObjectId(),\n 'inputClass' => \\HubSpot\\Client\\Crm\\Contacts\\Model\\SimplePublicObjectId::class,\n 'api' => $this->getNewInstance()->crm()->contacts()->batchApi(),\n ],\n ];\n\n if (! isset($configurations[$objectType])) {\n throw new \\InvalidArgumentException(\"Unsupported object type: {$objectType}\");\n }\n\n return $configurations[$objectType];\n }\n\n private function prepareBatchRequest(array $batchConfig, array $crmIds, array $fields): object\n {\n $batchReadRequest = $batchConfig['batchReadRequest'];\n $inputClass = $batchConfig['inputClass'];\n\n $inputs = array_map(function ($crmId) use ($inputClass) {\n $input = new $inputClass();\n $input->setId($crmId);\n\n return $input;\n }, $crmIds);\n\n $batchReadRequest->setInputs($inputs);\n $batchReadRequest->setProperties($fields);\n\n return $batchReadRequest;\n }\n\n private function validateApiResponse($response, string $objectType): void\n {\n if (! $response) {\n throw new CrmException(\"HubSpot API returned null response for {$objectType} batch read\");\n }\n }\n\n private function processApiResults($response): array\n {\n $results = [];\n $responseResults = $response->getResults();\n\n if ($responseResults) {\n foreach ($responseResults as $object) {\n if ($object && $object->getId()) {\n $results[$object->getId()] = [\n 'id' => $object->getId(),\n 'properties' => $object->getProperties() ?: [],\n ];\n }\n }\n }\n\n return $results;\n }\n\n private function logBatchResults(string $objectType, array $crmIds, array $results): void\n {\n $this->log->info(\"[HubSpot] Batch fetched {$objectType}\", [\n 'requested_count' => count($crmIds),\n 'returned_count' => count($results),\n 'crm_ids' => $crmIds,\n ]);\n }\n\n private function handleBatchError(\\Throwable $e, string $objectType, array $crmIds): void\n {\n $errorMessage = $e->getMessage() ?: 'Unknown error';\n $errorTrace = $e->getTraceAsString() ?: 'No trace available';\n\n $this->log->error(\"[HubSpot] Failed to batch fetch {$objectType}\", [\n 'crm_ids' => $crmIds,\n 'error' => $errorMessage,\n 'trace' => $errorTrace,\n ]);\n\n throw new CrmException(\"Failed to batch fetch {$objectType}: \" . $errorMessage);\n }\n\n /**\n * Batch read multiple opportunities by their CRM IDs\n *\n * @param array<string> $crmIds Array of HubSpot deal IDs (max 100)\n * @param array<string> $fields Array of property names to fetch\n *\n * @return array<string, array> Array keyed by CRM ID with opportunity data\n */\n public function getOpportunitiesByIds(array $crmIds, array $fields): array\n {\n return $this->batchReadObjects('deals', $crmIds, $fields);\n }\n\n /**\n * Batch read multiple companies by their CRM IDs\n *\n * @param array<string> $crmIds Array of HubSpot company IDs (max 100)\n * @param array<string> $fields Array of property names to fetch\n *\n * @return array<string, array> Array keyed by CRM ID with company data\n */\n public function getCompaniesByIds(array $crmIds, array $fields): array\n {\n return $this->batchReadObjects('companies', $crmIds, $fields);\n }\n\n /**\n * Batch read multiple contacts by their CRM IDs\n *\n * @param array<string> $crmIds Array of HubSpot contact IDs (max 100)\n * @param array<string> $fields Array of property names to fetch\n *\n * @return array<string, array> Array keyed by CRM ID with contact data\n */\n public function getContactsByIds(array $crmIds, array $fields): array\n {\n return $this->batchReadObjects('contacts', $crmIds, $fields);\n }\n\n /**\n * @throws CompanyApiException\n * @throws CrmException\n */\n public function getAccountById(string $crmId, array $fields): array\n {\n try {\n $company = $this->getNewInstance()->crm()->companies()->basicApi()->getById(\n $crmId,\n implode(',', $fields),\n );\n } catch (CompanyApiException $e) {\n $this->log->info('[Hubspot] Failed to fetch account', [\n 'crm_id' => $crmId,\n 'reason' => $e->getMessage(),\n ]);\n\n throw $e;\n }\n\n if (! $company instanceof CompaniesWithAssociations) {\n throw new CrmException('Account not found');\n }\n\n return [\n 'id' => $company->getId(),\n 'properties' => $company->getProperties(),\n ];\n }\n\n /**\n * @throws ContactApiException\n * @throws CrmException\n */\n public function getContactById(string $crmId, array $fields): array\n {\n try {\n $contact = $this->getNewInstance()->crm()->contacts()->basicApi()->getById(\n $crmId,\n implode(',', $fields)\n );\n } catch (ContactApiException $e) {\n $this->log->info('[Hubspot] Failed to fetch contact', [\n 'crm_id' => $crmId,\n 'reason' => $e->getMessage(),\n ]);\n\n throw $e;\n }\n\n if (! $contact instanceof ContactsWithAssociations) {\n throw new CrmException('Contact not found');\n }\n\n return [\n 'id' => $contact->getId(),\n 'properties' => $contact->getProperties(),\n ];\n }\n\n /**\n * This is email search request that Hubspot offers as GET (more generous quota)\n */\n public function getContactByEmail(string $email, array $fields = []): array\n {\n try {\n $contact = $this->getNewInstance()->crm()->contacts()->basicApi()->getById(\n $email,\n implode(',', $fields),\n null,\n false,\n 'email'\n );\n\n return [\n 'id' => $contact->getId(),\n 'properties' => $contact->getProperties(),\n ];\n } catch (ContactApiException $e) {\n $this->log->info('[Hubspot] Failed to fetch contact', [\n 'email' => $email,\n 'reason' => $e->getMessage(),\n ]);\n\n return [];\n }\n }\n\n /**\n * @throws CrmException\n */\n public function fetchProperty(string $objectType, string $propertyId): Property\n {\n $result = $this->getNewInstance()->crm()->properties()->coreApi()->getByName($objectType, $propertyId);\n\n if (! $result instanceof Property) {\n $this->log->error('[Hubspot] Failed to fetch property', [\n 'object_type' => $objectType,\n 'property_id' => $propertyId,\n 'reason' => $result->getMessage(),\n ]);\n\n throw new CrmException('Failed to fetch property');\n }\n\n return $result;\n }\n\n /**\n * @return array<CrmFieldOption>\n */\n public function fetchPropertyOptions(string $objectType, string $propertyId): array\n {\n /** @var array<CrmFieldOption> */\n return $this->fetchProperty($objectType, $propertyId)->getOptions();\n }\n\n /**\n * @return array<array{id:string, label:string, deleted:bool}>\n */\n public function fetchCallDispositions(): array\n {\n /** @var Response $response */\n $response = $this->getInstance()->engagements()->getCallDispositions();\n\n /**\n * @var array<array{\n * id:string,\n * label:string,\n * deleted: bool\n * }>\n */\n return $response->toArray();\n }\n\n /**\n * @return array<CrmFieldOption>\n */\n public function fetchOpportunityPipelineStages(): array\n {\n $stages = [];\n $apiResponse = $this->getNewInstance()->crm()->pipelines()->pipelinesApi()->getAll('deals');\n\n if ($apiResponse instanceof Error) {\n $this->log->error('[Hubspot] Failed to fetch opportunity pipelines', [\n 'reason' => $apiResponse->getMessage(),\n ]);\n\n return [];\n }\n\n foreach ($apiResponse->getResults() as $pipeline) {\n $pipelineStages = array_map(\n static function (PipelineStage $stage) {\n return [\n 'id' => $stage->getId(),\n 'label' => $stage->getLabel(),\n ];\n },\n $pipeline->getStages()\n );\n\n $stages = array_merge($stages, $pipelineStages);\n }\n\n return $stages;\n }\n\n public function fetchOpportunityPipelines(): array\n {\n $pipelines = [];\n\n try {\n $apiResponse = $this->makeRequest('/crm/v3/pipelines/deals');\n } catch (\\Exception $e) {\n $this->log->info('[Hubspot] Failed to fetch opportunity pipelines', [\n 'reason' => $e->getMessage(),\n ]);\n\n return [];\n }\n\n $response = $apiResponse->toArray();\n\n foreach ($response['results'] as $pipeline) {\n $pipelines[] = [\n 'id' => $pipeline['id'],\n 'label' => $pipeline['label'],\n ];\n }\n\n return $pipelines;\n }\n\n /**\n * @return array<CrmFieldOption>\n */\n public function fetchMeetingOutcomeFieldOptions(Field $field): array\n {\n return $field->getCrmProviderId() === 'meetingOutcome'\n ? $this->fetchMeetingOutcomeTypes()\n : $this->fetchCallActivityTypes();\n }\n\n public function fetchMeetingOutcomeTypes(): array\n {\n return $this->extractMeetingTypeOptions(\n 'https://api.hubapi.com/crm/v3/properties/meeting/hs_meeting_outcome'\n );\n }\n\n public function fetchCallActivityTypes(): array\n {\n return $this->extractMeetingTypeOptions(\n 'https://api.hubapi.com/crm/v3/properties/call/hs_activity_type'\n );\n }\n\n private function extractMeetingTypeOptions(string $endpoint): array\n {\n /** @var Response $response */\n $response = $this->getInstance()\n ->getClient()\n ->request('GET', $endpoint);\n\n /**\n * @var array<array{\n * value: string,\n * label: string,\n * displayOrder: int\n * }> $optionData\n */\n $optionData = $response->toArray()['options'] ?? [];\n\n $options = [];\n foreach ($optionData as $item) {\n $options[] = [\n 'id' => $item['value'],\n 'value' => $item['value'],\n 'label' => $item['label'],\n 'display_order' => $item['displayOrder'],\n ];\n }\n\n return $options;\n }\n\n /**\n * @return array<CrmFieldOption>\n */\n public function fetchDispositionFieldOptions(): array\n {\n $options = [];\n\n $dispositions = $this->fetchCallDispositions();\n\n foreach ($dispositions as $disposition) {\n if ($disposition['deleted'] !== false) {\n continue;\n }\n\n $option['value'] = $disposition['id'];\n $option['id'] = $disposition['id'];\n $option['label'] = $disposition['label'];\n\n $options[] = $option;\n }\n\n return $options;\n }\n\n /**\n * @return array<CrmFieldOption>\n */\n public function fetchOpportunityFieldOptions(Field $field): array\n {\n if ($field->isStageField()) {\n return $this->fetchOpportunityPipelineStages();\n }\n\n if ($field->isPipelineField()) {\n return $this->fetchOpportunityPipelines();\n }\n\n return $this->fetchPropertyOptions('deals', $field->getCrmProviderId());\n }\n\n /**\n * @throws BadRequest\n * @throws HubspotException\n */\n public function makeRequest(string $endpoint, $method = 'GET', $payload = [], ?string $queryString = null)\n {\n $endpoint = self::BASE_URL . $endpoint;\n\n if ($method === 'GET') {\n return $this->getInstance()->getClient()?->request(\n method: $method,\n endpoint: $endpoint,\n query_string: $queryString\n );\n } else {\n return $this->getInstance()->getClient()->request($method, $endpoint, [\n 'json' => ($payload),\n ]);\n }\n }\n\n /**\n * @throws BadRequest\n * @throws HubspotException\n */\n public function createMeeting(array $payload): Response\n {\n $endpoint = '/crm/v3/objects/meetings';\n\n return $this->makeRequest($endpoint, 'POST', $payload);\n }\n\n /**\n * @throws BadRequest\n * @throws HubspotException\n */\n public function updateMeeting(string $meetingId, array $payload): Response\n {\n $endpoint = '/crm/v3/objects/meetings/' . $meetingId;\n\n return $this->makeRequest($endpoint, 'PATCH', $payload);\n }\n\n /**\n * @throws \\Exception\n */\n public function createNote(\n string $body,\n string $ownerId,\n int $timestamp,\n string $objectId,\n NoteObject $noteObject\n ): ?string {\n try {\n $noteInput = new SimplePublicObjectInput([\n 'properties' => [\n 'hs_note_body' => $body,\n 'hubspot_owner_id' => $ownerId,\n 'hs_timestamp' => $timestamp,\n ],\n ]);\n\n // Create note\n $note = $this->getNewInstance()->crm()->objects()->basicApi()->create('note', $noteInput);\n\n $this->getNewInstance()->crm()->objects()->associationsApi()->create(\n 'note',\n $note->getId(),\n $this->getNoteObject($noteObject),\n $objectId,\n $this->getNoteAssociationType($noteObject),\n );\n\n return $note->getId();\n } catch (\\Exception $e) {\n $this->log->error('[Hubspot] Failed to create note', [\n 'objectId' => $objectId,\n 'noteObject' => $noteObject->getObjectType(),\n 'reason' => $e->getMessage(),\n ]);\n\n \\Sentry::captureException($e);\n }\n\n return null;\n }\n\n public function updateEngagement(string $objectId, array $engagement, array $metadata): void\n {\n $this->getInstance()->engagements()->update($objectId, $engagement, $metadata);\n }\n\n public function getEngagementData(string $engagementId): array\n {\n $engagement = $this->getInstance()->engagements()->get($engagementId);\n\n return $engagement->toArray();\n }\n\n public function createEngagement(array $engagement, array $associations, array $metadata): Response\n {\n return $this->getInstance()\n ->engagements()\n ->create($engagement, $associations, $metadata);\n }\n\n public function isUnauthorizedException(\\Exception $e): bool\n {\n // Check for specific HubSpot API exception types first\n if ($e instanceof BadRequest) {\n // BadRequest can contain 401 status codes\n return $e->getCode() === 401;\n }\n\n // Check for HTTP client exceptions with status codes\n if ($e instanceof \\GuzzleHttp\\Exception\\RequestException && $e->hasResponse()) {\n $response = $e->getResponse();\n if ($response !== null) {\n return $response->getStatusCode() === 401;\n }\n }\n\n // Check for Guzzle HTTP exceptions\n if ($e instanceof \\GuzzleHttp\\Exception\\ClientException) {\n return $e->getCode() === 401;\n }\n\n // Fallback to string matching as last resort, but be more specific\n $message = strtolower($e->getMessage());\n\n return str_contains($message, '401 unauthorized') ||\n str_contains($message, 'http 401') ||\n str_contains($message, 'status code 401') ||\n (preg_match('/\\b401\\b/', $message) && str_contains($message, 'unauthorized'));\n }\n\n /**\n * Validates and refreshes the access token if needed before API requests.\n * This ensures long-running processes don't fail due to token expiration.\n *\n * @throws SocialAccountTokenInvalidException\n */\n public function ensureValidToken(): void\n {\n if ($this->oauthAccount === null) {\n return;\n }\n\n $newToken = $this->tokenManager->ensureValidToken($this->oauthAccount);\n if ($newToken !== null) {\n $this->accessToken = $newToken;\n }\n }\n\n public function getConfig()\n {\n return $this->config;\n }\n\n // returns only active (archived=false)\n public function getOwners(): array\n {\n return $this->getNewInstance()->crm()->owners()->getAll();\n }\n\n /**\n * @param bool $archived\n *\n * @return array<Owner>|[]\n */\n public function getOwnersArchived(bool $archived = true): array\n {\n $endpoint = '/crm/v3/owners';\n $queryParams = [\n 'archived' => $archived ? 'true' : 'false',\n ];\n $queryString = http_build_query($queryParams);\n\n $owners = [];\n\n try {\n $response = $this->makeRequest(endpoint: $endpoint, queryString: $queryString);\n $responseData = $response?->toArray();\n\n foreach ($responseData['results'] as $result) {\n try {\n $owners[] = Owner::create($result);\n } catch (Throwable $e) {\n $this->log->error('[HubSpot] Failed to process owner data', [\n 'result' => $result,\n 'error' => $e->getMessage(),\n ]);\n\n continue;\n }\n }\n } catch (Throwable $e) {\n $this->log->error('HubSpot] Failed to fetch owners', [\n 'archived' => $archived,\n 'error' => $e->getMessage(),\n ]);\n\n return [];\n }\n\n return $owners;\n }\n\n public function getMeeting(string $engagementId): ObjectWithAssociations\n {\n return $this->getNewInstance()->crm()->objects()->basicApi()\n ->getById('meeting', $engagementId, null, 'contact,company,deal');\n }\n\n public function deleteEngagement(string $engagementId): void\n {\n $this->getInstance()->engagements()->delete((int) $engagementId);\n }\n\n public function getAssociationsData(array $ids, string $fromObject, string $toObject): array\n {\n $associationData = [];\n $idChunks = array_chunk($ids, self::ASSOCIATIONS_BATCH_SIZE_LIMIT);\n\n foreach ($idChunks as $idChunk) {\n try {\n $batchInput = new \\HubSpot\\Client\\Crm\\Associations\\Model\\BatchInputPublicObjectId();\n $batchInput->setInputs(array_map(function ($id) {\n $publicObjectId = new \\HubSpot\\Client\\Crm\\Associations\\Model\\PublicObjectId();\n $publicObjectId->setId($id);\n\n return $publicObjectId;\n }, $idChunk));\n\n $associatedObjectsData = $this\n ->getNewInstance()\n ->crm()\n ->associations()\n ->batchApi()\n ->read($fromObject, $toObject, $batchInput);\n\n if ($associatedObjectsData instanceof \\HubSpot\\Client\\Crm\\Associations\\Model\\BatchResponsePublicAssociationMulti) {\n foreach ($associatedObjectsData->getResults() as $association) {\n $from = $association->getFrom()->getId();\n $toAssociations = $association->getTo();\n\n if (! empty($toAssociations)) {\n $associationData[$from] = array_map(function ($item) {\n return $item->getId();\n }, $toAssociations);\n }\n }\n }\n// } catch (RateLimitException $e) {\n// throw $e;\n } catch (\\Exception $e) {\n $this->log->error('[Hubspot] Failed to fetch associations', [\n 'from_object' => $fromObject,\n 'to_object' => $toObject,\n 'reason' => $e->getMessage(),\n ]);\n }\n }\n\n return $associationData;\n }\n\n /**\n * @throws \\Exception\n */\n private function getNoteAssociationType(NoteObject $noteObject): string\n {\n return match($noteObject) {\n NoteObject::Opportunity => 'note_to_deal',\n NoteObject::Lead, NoteObject::Contact => 'note_to_contact', // or 'note_to_lead' if your portal supports it\n NoteObject::Account => 'note_to_company',\n NoteObject::Call, NoteObject::Event => throw new \\Exception('Not supported'),\n };\n }\n\n /**\n * @throws \\Exception\n */\n private function getNoteObject(NoteObject $noteObject): string\n {\n return match($noteObject) {\n NoteObject::Opportunity => 'deal',\n NoteObject::Lead, NoteObject::Contact => 'contact',\n NoteObject::Account => 'company',\n NoteObject::Call, NoteObject::Event => throw new \\Exception('Not supported'),\n };\n }\n\n public function addAssociations(string $objectType, string $associationType, array $payload): Response\n {\n $endpoint = \"/crm/v4/associations/$objectType/$associationType/batch/create\";\n\n return $this->makeRequest($endpoint, 'POST', $payload);\n }\n\n public function removeAssociations(string $objectType, string $associationType, array $payload): Response\n {\n $endpoint = \"/crm/v4/associations/$objectType/$associationType/batch/archive\";\n\n return $this->makeRequest($endpoint, 'POST', $payload);\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Services\\Crm\\Hubspot;\n\nuse HubSpot\\Client\\Crm\\Deals\\ApiException as DealApiException;\nuse HubSpot\\Client\\Crm\\Contacts\\ApiException as ContactApiException;\nuse HubSpot\\Client\\Crm\\Companies\\ApiException as CompanyApiException;\nuse HubSpot\\Client\\Crm\\Contacts\\Model\\SimplePublicObjectWithAssociations as ContactsWithAssociations;\nuse HubSpot\\Client\\Crm\\Companies\\Model\\SimplePublicObjectWithAssociations as CompaniesWithAssociations;\nuse HubSpot\\Client\\Crm\\Deals\\Model\\SimplePublicObjectWithAssociations as DealWithAssociations;\nuse HubSpot\\Client\\Crm\\Objects\\Model\\SimplePublicObjectInput;\nuse HubSpot\\Client\\Crm\\Objects\\Model\\SimplePublicObjectWithAssociations as ObjectWithAssociations;\nuse HubSpot\\Client\\Crm\\Pipelines\\Model\\Error;\nuse HubSpot\\Client\\Crm\\Pipelines\\Model\\PipelineStage;\nuse HubSpot\\Client\\Crm\\Properties\\Model\\Property;\nuse HubSpot\\Discovery\\Discovery;\nuse Jiminny\\Exceptions\\CrmException;\nuse Jiminny\\Exceptions\\RateLimitException;\nuse Jiminny\\Exceptions\\SocialAccountTokenInvalidException;\nuse Jiminny\\Jobs\\Crm\\NoteObject;\nuse Jiminny\\Models\\Crm\\Field;\nuse Jiminny\\Services\\Crm\\BaseClient;\nuse Jiminny\\Services\\Crm\\Hubspot\\DTO\\Response\\Owner;\nuse Jiminny\\Services\\SocialAccountService;\nuse SevenShores\\Hubspot\\Exceptions\\BadRequest;\nuse SevenShores\\Hubspot\\Exceptions\\HubspotException;\nuse SevenShores\\Hubspot\\Factory;\nuse SevenShores\\Hubspot\\Http\\Response;\nuse Jiminny\\Services\\Crm\\Hubspot\\Pagination\\HubspotPaginationService;\nuse Illuminate\\Support\\Facades\\Redis;\nuse Throwable;\n\n/**\n * @phpstan-type CrmFieldOption array{id:string, label:string, value?:string}\n */\nclass Client extends BaseClient implements HubspotClientInterface\n{\n public const string MIN_API_VERSION = '2';\n\n public const string BASE_URL = 'https://api.hubapi.com';\n\n public const int ASSOCIATIONS_BATCH_SIZE_LIMIT = 1000;\n\n private HubspotPaginationService $paginationService;\n private HubspotTokenManager $tokenManager;\n\n public function __construct(\n SocialAccountService $socialAccountService,\n HubspotPaginationService $paginationService,\n HubspotTokenManager $tokenManager\n ) {\n parent::__construct($socialAccountService);\n $this->paginationService = $paginationService;\n $this->tokenManager = $tokenManager;\n\n $this->setBaseUrl(self::BASE_URL);\n $this->setVersion(self::MIN_API_VERSION);\n }\n\n /**\n * Reacts to a rate limits (429) from HubSpot by translating it\n * into a RateLimitException carrying retry_after.\n *\n * Wrap any outbound HubSpot call (SDK or raw HTTP) like:\n *\n * $this->executeRequest(fn () => $this->getNewInstance()->crm()->...);\n *\n * @template T\n * @param callable(): T $apiCall\n * @return T\n *\n * @throws RateLimitException\n */\n private function executeRequest(callable $apiCall)\n {\n $cacheKey = $this->getRateLimitCacheKey();\n\n $cachedRetryAfter = Redis::get($cacheKey);\n if (is_string($cachedRetryAfter) && is_numeric($cachedRetryAfter)) {\n throw new RateLimitException(\n 'Hubspot rate limit (cached circuit-breaker)',\n (int) $cachedRetryAfter,\n );\n }\n\n try {\n return $apiCall();\n } catch (Throwable $e) {\n if ($this->isHubspotRateLimit($e)) {\n $retryAfter = $this->parseRetryAfter($e);\n\n Redis::setex($cacheKey, $retryAfter, (string) $retryAfter);\n\n $this->log->warning('[Hubspot] Received 429 from API', [\n 'team_id' => $this->config->team_id,\n 'config_id' => $this->config->getId(),\n 'retry_after' => $retryAfter,\n 'message' => $this->parseMessage($e),\n 'reason' => $e->getMessage(),\n ]);\n\n throw new RateLimitException('Hubspot returned 429', $retryAfter, $e);\n }\n\n throw $e;\n }\n }\n\n private function getRateLimitCacheKey(): string\n {\n return sprintf('hubspot:ratelimit:portal:%d', $this->config->getId());\n }\n\n public function isHubspotRateLimit(Throwable $e): bool\n {\n if ($e instanceof BadRequest\n || $e instanceof DealApiException\n || $e instanceof ContactApiException\n || $e instanceof CompanyApiException\n || $e instanceof \\GuzzleHttp\\Exception\\RequestException\n ) {\n return (int) $e->getCode() === 429;\n }\n\n return false;\n }\n\n public function parseRetryAfter(Throwable $e): int\n {\n if (method_exists($e, 'getResponseHeaders')) {\n $headers = $e->getResponseHeaders() ?: [];\n $value = $headers['Retry-After'] ?? $headers['retry-after'] ?? null;\n if (is_array($value)) {\n $value = $value[0] ?? null;\n }\n if (is_numeric($value)) {\n return (int) $value;\n }\n }\n\n $message = strtolower($this->parseMessage($e));\n\n if (str_contains($message, 'daily')) {\n return 600;\n }\n if (str_contains($message, 'ten secondly')) {\n return 10;\n }\n if (str_contains($message, 'secondly')) {\n return 1;\n }\n\n $this->log->warning('[Hubspot] No retry-after header or known message, using default', [\n 'exception_class' => get_class($e),\n 'message' => $message,\n ]);\n\n return 10;\n }\n\n public function parseMessage(Throwable $e): string\n {\n if (! method_exists($e, 'getResponseBody')) {\n return '';\n }\n\n $body = $e->getResponseBody();\n if (is_string($body)) {\n $body = json_decode($body, true) ?? [];\n }\n\n if (! is_array($body)) {\n return '';\n }\n\n return (string) ($body['message'] ?? '');\n }\n\n public function getMinimumApiVersion(): string\n {\n return self::MIN_API_VERSION;\n }\n\n public function getInstance(): Factory\n {\n return new Factory([\n 'key' => $this->accessToken,\n 'oauth2' => true,\n 'base_url' => $this->baseUrl,\n ]);\n }\n\n public function getNewInstance(): Discovery\n {\n return \\HubSpot\\Factory::createWithAccessToken($this->accessToken);\n }\n\n /**\n * Secondly and daily limits for Hubspot API\n *\n * Product Tier: Free & Starter | Professional & Enterprise | API add-on (any tier)\n * Burst: 100/10 seconds | 150/10 seconds | 200/10 seconds\n * Daily: 250,000 | 500,000 | 1,000,000\n *\n * Official documentation states: The search endpoints are rate limited to five requests per second.\n * Since with 5 RPS were still hitting secondly rate limits we lowered it to 4\n */\n public function getPaginatedData(array $payload, string $type, int $offset = 0): array\n {\n $total = 0;\n $lastId = null;\n $rows = [];\n foreach ($this->getPaginatedDataGenerator($payload, $type, $offset, $total, $lastId) as $row) {\n $rows[] = $row;\n }\n\n return ['results' => $rows, 'total' => $total, 'last_record' => $lastId];\n }\n\n /**\n * @throws HubspotException\n * @throws SocialAccountTokenInvalidException\n * @throws BadRequest\n */\n public function getPaginatedDataGenerator(\n array $payload,\n string $type,\n int $offset = 0,\n int &$total = 0,\n ?string &$lastRecordId = null\n ): \\Generator {\n return $this->paginationService->getPaginatedDataGenerator(\n $this,\n $payload,\n $type,\n $offset,\n $total,\n $lastRecordId\n );\n }\n\n /**\n * Execute a search request against HubSpot CRM objects with rate limiting.\n *\n * @param string $objectType The object type ('deals', 'companies', 'contacts', 'calls')\n * @param array<string, mixed> $payload The search payload with filters, sorts, properties, etc.\n * @return array The search response with 'results', 'total', 'paging' keys\n * @throws RateLimitException When rate limit is hit\n * @throws HubspotException On API errors\n */\n public function search(string $objectType, array $payload): array\n {\n $endpoint = self::BASE_URL . \"/crm/v3/objects/{$objectType}/search\";\n\n return $this->executeRequest(function () use ($endpoint, $payload) {\n $response = $this->getInstance()->getClient()->request('POST', $endpoint, ['json' => $payload]);\n\n return $response->toArray();\n });\n }\n\n /**\n * @throws DealApiException\n * @throws CrmException\n */\n public function getOpportunityById(string $crmId, array $fields): array\n {\n try {\n// $deal = $this->executeRequest(fn () => $this->getNewInstance()->crm()->deals()->basicApi()->getById(\n $deal = $this->getNewInstance()->crm()->deals()->basicApi()->getById(\n $crmId,\n implode(',', $fields),\n 'companies,contacts'\n );\n } catch (DealApiException $e) {\n $this->log->info('[Hubspot] Failed to fetch opportunity', [\n 'crm_id' => $crmId,\n 'reason' => $e->getMessage(),\n ]);\n\n throw $e;\n }\n\n if (! $deal instanceof DealWithAssociations) {\n throw new CrmException('Deal not found');\n }\n\n return [\n 'id' => $deal->getId(),\n 'properties' => $deal->getProperties(),\n 'associations' => $deal->getAssociations(),\n ];\n }\n\n /**\n * Generic batch read method for HubSpot objects\n *\n * @param string $objectType The object type ('deals', 'companies', 'contacts')\n * @param array<string> $crmIds Array of HubSpot object IDs (max 100)\n * @param array<string> $fields Array of property names to fetch\n *\n * @return array<string, array> Array keyed by CRM ID with object data\n */\n private function batchReadObjects(string $objectType, array $crmIds, array $fields): array\n {\n if (empty($crmIds)) {\n return [];\n }\n\n $this->validateBatchSize($objectType, $crmIds);\n $this->ensureValidToken();\n\n try {\n $batchConfig = $this->createBatchConfiguration($objectType);\n $batchReadRequest = $this->prepareBatchRequest($batchConfig, $crmIds, $fields);\n $response = $batchConfig['api']->read($batchReadRequest);\n\n $this->validateApiResponse($response, $objectType);\n\n $results = $this->processApiResults($response);\n $this->logBatchResults($objectType, $crmIds, $results);\n\n return $results;\n } catch (\\Throwable $e) {\n $this->handleBatchError($e, $objectType, $crmIds);\n }\n }\n\n private function validateBatchSize(string $objectType, array $crmIds): void\n {\n if (count($crmIds) > 100) {\n throw new \\InvalidArgumentException(\"Batch size cannot exceed 100 {$objectType}\");\n }\n }\n\n private function createBatchConfiguration(string $objectType): array\n {\n $configurations = [\n 'deals' => [\n 'batchReadRequest' => new \\HubSpot\\Client\\Crm\\Deals\\Model\\BatchReadInputSimplePublicObjectId(),\n 'inputClass' => \\HubSpot\\Client\\Crm\\Deals\\Model\\SimplePublicObjectId::class,\n 'api' => $this->getNewInstance()->crm()->deals()->batchApi(),\n ],\n 'companies' => [\n 'batchReadRequest' => new \\HubSpot\\Client\\Crm\\Companies\\Model\\BatchReadInputSimplePublicObjectId(),\n 'inputClass' => \\HubSpot\\Client\\Crm\\Companies\\Model\\SimplePublicObjectId::class,\n 'api' => $this->getNewInstance()->crm()->companies()->batchApi(),\n ],\n 'contacts' => [\n 'batchReadRequest' => new \\HubSpot\\Client\\Crm\\Contacts\\Model\\BatchReadInputSimplePublicObjectId(),\n 'inputClass' => \\HubSpot\\Client\\Crm\\Contacts\\Model\\SimplePublicObjectId::class,\n 'api' => $this->getNewInstance()->crm()->contacts()->batchApi(),\n ],\n ];\n\n if (! isset($configurations[$objectType])) {\n throw new \\InvalidArgumentException(\"Unsupported object type: {$objectType}\");\n }\n\n return $configurations[$objectType];\n }\n\n private function prepareBatchRequest(array $batchConfig, array $crmIds, array $fields): object\n {\n $batchReadRequest = $batchConfig['batchReadRequest'];\n $inputClass = $batchConfig['inputClass'];\n\n $inputs = array_map(function ($crmId) use ($inputClass) {\n $input = new $inputClass();\n $input->setId($crmId);\n\n return $input;\n }, $crmIds);\n\n $batchReadRequest->setInputs($inputs);\n $batchReadRequest->setProperties($fields);\n\n return $batchReadRequest;\n }\n\n private function validateApiResponse($response, string $objectType): void\n {\n if (! $response) {\n throw new CrmException(\"HubSpot API returned null response for {$objectType} batch read\");\n }\n }\n\n private function processApiResults($response): array\n {\n $results = [];\n $responseResults = $response->getResults();\n\n if ($responseResults) {\n foreach ($responseResults as $object) {\n if ($object && $object->getId()) {\n $results[$object->getId()] = [\n 'id' => $object->getId(),\n 'properties' => $object->getProperties() ?: [],\n ];\n }\n }\n }\n\n return $results;\n }\n\n private function logBatchResults(string $objectType, array $crmIds, array $results): void\n {\n $this->log->info(\"[HubSpot] Batch fetched {$objectType}\", [\n 'requested_count' => count($crmIds),\n 'returned_count' => count($results),\n 'crm_ids' => $crmIds,\n ]);\n }\n\n private function handleBatchError(\\Throwable $e, string $objectType, array $crmIds): void\n {\n $errorMessage = $e->getMessage() ?: 'Unknown error';\n $errorTrace = $e->getTraceAsString() ?: 'No trace available';\n\n $this->log->error(\"[HubSpot] Failed to batch fetch {$objectType}\", [\n 'crm_ids' => $crmIds,\n 'error' => $errorMessage,\n 'trace' => $errorTrace,\n ]);\n\n throw new CrmException(\"Failed to batch fetch {$objectType}: \" . $errorMessage);\n }\n\n /**\n * Batch read multiple opportunities by their CRM IDs\n *\n * @param array<string> $crmIds Array of HubSpot deal IDs (max 100)\n * @param array<string> $fields Array of property names to fetch\n *\n * @return array<string, array> Array keyed by CRM ID with opportunity data\n */\n public function getOpportunitiesByIds(array $crmIds, array $fields): array\n {\n return $this->batchReadObjects('deals', $crmIds, $fields);\n }\n\n /**\n * Batch read multiple companies by their CRM IDs\n *\n * @param array<string> $crmIds Array of HubSpot company IDs (max 100)\n * @param array<string> $fields Array of property names to fetch\n *\n * @return array<string, array> Array keyed by CRM ID with company data\n */\n public function getCompaniesByIds(array $crmIds, array $fields): array\n {\n return $this->batchReadObjects('companies', $crmIds, $fields);\n }\n\n /**\n * Batch read multiple contacts by their CRM IDs\n *\n * @param array<string> $crmIds Array of HubSpot contact IDs (max 100)\n * @param array<string> $fields Array of property names to fetch\n *\n * @return array<string, array> Array keyed by CRM ID with contact data\n */\n public function getContactsByIds(array $crmIds, array $fields): array\n {\n return $this->batchReadObjects('contacts', $crmIds, $fields);\n }\n\n /**\n * @throws CompanyApiException\n * @throws CrmException\n */\n public function getAccountById(string $crmId, array $fields): array\n {\n try {\n $company = $this->getNewInstance()->crm()->companies()->basicApi()->getById(\n $crmId,\n implode(',', $fields),\n );\n } catch (CompanyApiException $e) {\n $this->log->info('[Hubspot] Failed to fetch account', [\n 'crm_id' => $crmId,\n 'reason' => $e->getMessage(),\n ]);\n\n throw $e;\n }\n\n if (! $company instanceof CompaniesWithAssociations) {\n throw new CrmException('Account not found');\n }\n\n return [\n 'id' => $company->getId(),\n 'properties' => $company->getProperties(),\n ];\n }\n\n /**\n * @throws ContactApiException\n * @throws CrmException\n */\n public function getContactById(string $crmId, array $fields): array\n {\n try {\n $contact = $this->getNewInstance()->crm()->contacts()->basicApi()->getById(\n $crmId,\n implode(',', $fields)\n );\n } catch (ContactApiException $e) {\n $this->log->info('[Hubspot] Failed to fetch contact', [\n 'crm_id' => $crmId,\n 'reason' => $e->getMessage(),\n ]);\n\n throw $e;\n }\n\n if (! $contact instanceof ContactsWithAssociations) {\n throw new CrmException('Contact not found');\n }\n\n return [\n 'id' => $contact->getId(),\n 'properties' => $contact->getProperties(),\n ];\n }\n\n /**\n * This is email search request that Hubspot offers as GET (more generous quota)\n */\n public function getContactByEmail(string $email, array $fields = []): array\n {\n try {\n $contact = $this->getNewInstance()->crm()->contacts()->basicApi()->getById(\n $email,\n implode(',', $fields),\n null,\n false,\n 'email'\n );\n\n return [\n 'id' => $contact->getId(),\n 'properties' => $contact->getProperties(),\n ];\n } catch (ContactApiException $e) {\n $this->log->info('[Hubspot] Failed to fetch contact', [\n 'email' => $email,\n 'reason' => $e->getMessage(),\n ]);\n\n return [];\n }\n }\n\n /**\n * @throws CrmException\n */\n public function fetchProperty(string $objectType, string $propertyId): Property\n {\n $result = $this->getNewInstance()->crm()->properties()->coreApi()->getByName($objectType, $propertyId);\n\n if (! $result instanceof Property) {\n $this->log->error('[Hubspot] Failed to fetch property', [\n 'object_type' => $objectType,\n 'property_id' => $propertyId,\n 'reason' => $result->getMessage(),\n ]);\n\n throw new CrmException('Failed to fetch property');\n }\n\n return $result;\n }\n\n /**\n * @return array<CrmFieldOption>\n */\n public function fetchPropertyOptions(string $objectType, string $propertyId): array\n {\n /** @var array<CrmFieldOption> */\n return $this->fetchProperty($objectType, $propertyId)->getOptions();\n }\n\n /**\n * @return array<array{id:string, label:string, deleted:bool}>\n */\n public function fetchCallDispositions(): array\n {\n /** @var Response $response */\n $response = $this->getInstance()->engagements()->getCallDispositions();\n\n /**\n * @var array<array{\n * id:string,\n * label:string,\n * deleted: bool\n * }>\n */\n return $response->toArray();\n }\n\n /**\n * @return array<CrmFieldOption>\n */\n public function fetchOpportunityPipelineStages(): array\n {\n $stages = [];\n $apiResponse = $this->getNewInstance()->crm()->pipelines()->pipelinesApi()->getAll('deals');\n\n if ($apiResponse instanceof Error) {\n $this->log->error('[Hubspot] Failed to fetch opportunity pipelines', [\n 'reason' => $apiResponse->getMessage(),\n ]);\n\n return [];\n }\n\n foreach ($apiResponse->getResults() as $pipeline) {\n $pipelineStages = array_map(\n static function (PipelineStage $stage) {\n return [\n 'id' => $stage->getId(),\n 'label' => $stage->getLabel(),\n ];\n },\n $pipeline->getStages()\n );\n\n $stages = array_merge($stages, $pipelineStages);\n }\n\n return $stages;\n }\n\n public function fetchOpportunityPipelines(): array\n {\n $pipelines = [];\n\n try {\n $apiResponse = $this->makeRequest('/crm/v3/pipelines/deals');\n } catch (\\Exception $e) {\n $this->log->info('[Hubspot] Failed to fetch opportunity pipelines', [\n 'reason' => $e->getMessage(),\n ]);\n\n return [];\n }\n\n $response = $apiResponse->toArray();\n\n foreach ($response['results'] as $pipeline) {\n $pipelines[] = [\n 'id' => $pipeline['id'],\n 'label' => $pipeline['label'],\n ];\n }\n\n return $pipelines;\n }\n\n /**\n * @return array<CrmFieldOption>\n */\n public function fetchMeetingOutcomeFieldOptions(Field $field): array\n {\n return $field->getCrmProviderId() === 'meetingOutcome'\n ? $this->fetchMeetingOutcomeTypes()\n : $this->fetchCallActivityTypes();\n }\n\n public function fetchMeetingOutcomeTypes(): array\n {\n return $this->extractMeetingTypeOptions(\n 'https://api.hubapi.com/crm/v3/properties/meeting/hs_meeting_outcome'\n );\n }\n\n public function fetchCallActivityTypes(): array\n {\n return $this->extractMeetingTypeOptions(\n 'https://api.hubapi.com/crm/v3/properties/call/hs_activity_type'\n );\n }\n\n private function extractMeetingTypeOptions(string $endpoint): array\n {\n /** @var Response $response */\n $response = $this->getInstance()\n ->getClient()\n ->request('GET', $endpoint);\n\n /**\n * @var array<array{\n * value: string,\n * label: string,\n * displayOrder: int\n * }> $optionData\n */\n $optionData = $response->toArray()['options'] ?? [];\n\n $options = [];\n foreach ($optionData as $item) {\n $options[] = [\n 'id' => $item['value'],\n 'value' => $item['value'],\n 'label' => $item['label'],\n 'display_order' => $item['displayOrder'],\n ];\n }\n\n return $options;\n }\n\n /**\n * @return array<CrmFieldOption>\n */\n public function fetchDispositionFieldOptions(): array\n {\n $options = [];\n\n $dispositions = $this->fetchCallDispositions();\n\n foreach ($dispositions as $disposition) {\n if ($disposition['deleted'] !== false) {\n continue;\n }\n\n $option['value'] = $disposition['id'];\n $option['id'] = $disposition['id'];\n $option['label'] = $disposition['label'];\n\n $options[] = $option;\n }\n\n return $options;\n }\n\n /**\n * @return array<CrmFieldOption>\n */\n public function fetchOpportunityFieldOptions(Field $field): array\n {\n if ($field->isStageField()) {\n return $this->fetchOpportunityPipelineStages();\n }\n\n if ($field->isPipelineField()) {\n return $this->fetchOpportunityPipelines();\n }\n\n return $this->fetchPropertyOptions('deals', $field->getCrmProviderId());\n }\n\n /**\n * @throws BadRequest\n * @throws HubspotException\n */\n public function makeRequest(string $endpoint, $method = 'GET', $payload = [], ?string $queryString = null)\n {\n $endpoint = self::BASE_URL . $endpoint;\n\n if ($method === 'GET') {\n return $this->getInstance()->getClient()?->request(\n method: $method,\n endpoint: $endpoint,\n query_string: $queryString\n );\n } else {\n return $this->getInstance()->getClient()->request($method, $endpoint, [\n 'json' => ($payload),\n ]);\n }\n }\n\n /**\n * @throws BadRequest\n * @throws HubspotException\n */\n public function createMeeting(array $payload): Response\n {\n $endpoint = '/crm/v3/objects/meetings';\n\n return $this->makeRequest($endpoint, 'POST', $payload);\n }\n\n /**\n * @throws BadRequest\n * @throws HubspotException\n */\n public function updateMeeting(string $meetingId, array $payload): Response\n {\n $endpoint = '/crm/v3/objects/meetings/' . $meetingId;\n\n return $this->makeRequest($endpoint, 'PATCH', $payload);\n }\n\n /**\n * @throws \\Exception\n */\n public function createNote(\n string $body,\n string $ownerId,\n int $timestamp,\n string $objectId,\n NoteObject $noteObject\n ): ?string {\n try {\n $noteInput = new SimplePublicObjectInput([\n 'properties' => [\n 'hs_note_body' => $body,\n 'hubspot_owner_id' => $ownerId,\n 'hs_timestamp' => $timestamp,\n ],\n ]);\n\n // Create note\n $note = $this->getNewInstance()->crm()->objects()->basicApi()->create('note', $noteInput);\n\n $this->getNewInstance()->crm()->objects()->associationsApi()->create(\n 'note',\n $note->getId(),\n $this->getNoteObject($noteObject),\n $objectId,\n $this->getNoteAssociationType($noteObject),\n );\n\n return $note->getId();\n } catch (\\Exception $e) {\n $this->log->error('[Hubspot] Failed to create note', [\n 'objectId' => $objectId,\n 'noteObject' => $noteObject->getObjectType(),\n 'reason' => $e->getMessage(),\n ]);\n\n \\Sentry::captureException($e);\n }\n\n return null;\n }\n\n public function updateEngagement(string $objectId, array $engagement, array $metadata): void\n {\n $this->getInstance()->engagements()->update($objectId, $engagement, $metadata);\n }\n\n public function getEngagementData(string $engagementId): array\n {\n $engagement = $this->getInstance()->engagements()->get($engagementId);\n\n return $engagement->toArray();\n }\n\n public function createEngagement(array $engagement, array $associations, array $metadata): Response\n {\n return $this->getInstance()\n ->engagements()\n ->create($engagement, $associations, $metadata);\n }\n\n public function isUnauthorizedException(\\Exception $e): bool\n {\n // Check for specific HubSpot API exception types first\n if ($e instanceof BadRequest) {\n // BadRequest can contain 401 status codes\n return $e->getCode() === 401;\n }\n\n // Check for HTTP client exceptions with status codes\n if ($e instanceof \\GuzzleHttp\\Exception\\RequestException && $e->hasResponse()) {\n $response = $e->getResponse();\n if ($response !== null) {\n return $response->getStatusCode() === 401;\n }\n }\n\n // Check for Guzzle HTTP exceptions\n if ($e instanceof \\GuzzleHttp\\Exception\\ClientException) {\n return $e->getCode() === 401;\n }\n\n // Fallback to string matching as last resort, but be more specific\n $message = strtolower($e->getMessage());\n\n return str_contains($message, '401 unauthorized') ||\n str_contains($message, 'http 401') ||\n str_contains($message, 'status code 401') ||\n (preg_match('/\\b401\\b/', $message) && str_contains($message, 'unauthorized'));\n }\n\n /**\n * Validates and refreshes the access token if needed before API requests.\n * This ensures long-running processes don't fail due to token expiration.\n *\n * @throws SocialAccountTokenInvalidException\n */\n public function ensureValidToken(): void\n {\n if ($this->oauthAccount === null) {\n return;\n }\n\n $newToken = $this->tokenManager->ensureValidToken($this->oauthAccount);\n if ($newToken !== null) {\n $this->accessToken = $newToken;\n }\n }\n\n public function getConfig()\n {\n return $this->config;\n }\n\n // returns only active (archived=false)\n public function getOwners(): array\n {\n return $this->getNewInstance()->crm()->owners()->getAll();\n }\n\n /**\n * @param bool $archived\n *\n * @return array<Owner>|[]\n */\n public function getOwnersArchived(bool $archived = true): array\n {\n $endpoint = '/crm/v3/owners';\n $queryParams = [\n 'archived' => $archived ? 'true' : 'false',\n ];\n $queryString = http_build_query($queryParams);\n\n $owners = [];\n\n try {\n $response = $this->makeRequest(endpoint: $endpoint, queryString: $queryString);\n $responseData = $response?->toArray();\n\n foreach ($responseData['results'] as $result) {\n try {\n $owners[] = Owner::create($result);\n } catch (Throwable $e) {\n $this->log->error('[HubSpot] Failed to process owner data', [\n 'result' => $result,\n 'error' => $e->getMessage(),\n ]);\n\n continue;\n }\n }\n } catch (Throwable $e) {\n $this->log->error('HubSpot] Failed to fetch owners', [\n 'archived' => $archived,\n 'error' => $e->getMessage(),\n ]);\n\n return [];\n }\n\n return $owners;\n }\n\n public function getMeeting(string $engagementId): ObjectWithAssociations\n {\n return $this->getNewInstance()->crm()->objects()->basicApi()\n ->getById('meeting', $engagementId, null, 'contact,company,deal');\n }\n\n public function deleteEngagement(string $engagementId): void\n {\n $this->getInstance()->engagements()->delete((int) $engagementId);\n }\n\n public function getAssociationsData(array $ids, string $fromObject, string $toObject): array\n {\n $associationData = [];\n $idChunks = array_chunk($ids, self::ASSOCIATIONS_BATCH_SIZE_LIMIT);\n\n foreach ($idChunks as $idChunk) {\n try {\n $batchInput = new \\HubSpot\\Client\\Crm\\Associations\\Model\\BatchInputPublicObjectId();\n $batchInput->setInputs(array_map(function ($id) {\n $publicObjectId = new \\HubSpot\\Client\\Crm\\Associations\\Model\\PublicObjectId();\n $publicObjectId->setId($id);\n\n return $publicObjectId;\n }, $idChunk));\n\n $associatedObjectsData = $this\n ->getNewInstance()\n ->crm()\n ->associations()\n ->batchApi()\n ->read($fromObject, $toObject, $batchInput);\n\n if ($associatedObjectsData instanceof \\HubSpot\\Client\\Crm\\Associations\\Model\\BatchResponsePublicAssociationMulti) {\n foreach ($associatedObjectsData->getResults() as $association) {\n $from = $association->getFrom()->getId();\n $toAssociations = $association->getTo();\n\n if (! empty($toAssociations)) {\n $associationData[$from] = array_map(function ($item) {\n return $item->getId();\n }, $toAssociations);\n }\n }\n }\n// } catch (RateLimitException $e) {\n// throw $e;\n } catch (\\Exception $e) {\n $this->log->error('[Hubspot] Failed to fetch associations', [\n 'from_object' => $fromObject,\n 'to_object' => $toObject,\n 'reason' => $e->getMessage(),\n ]);\n }\n }\n\n return $associationData;\n }\n\n /**\n * @throws \\Exception\n */\n private function getNoteAssociationType(NoteObject $noteObject): string\n {\n return match($noteObject) {\n NoteObject::Opportunity => 'note_to_deal',\n NoteObject::Lead, NoteObject::Contact => 'note_to_contact', // or 'note_to_lead' if your portal supports it\n NoteObject::Account => 'note_to_company',\n NoteObject::Call, NoteObject::Event => throw new \\Exception('Not supported'),\n };\n }\n\n /**\n * @throws \\Exception\n */\n private function getNoteObject(NoteObject $noteObject): string\n {\n return match($noteObject) {\n NoteObject::Opportunity => 'deal',\n NoteObject::Lead, NoteObject::Contact => 'contact',\n NoteObject::Account => 'company',\n NoteObject::Call, NoteObject::Event => throw new \\Exception('Not supported'),\n };\n }\n\n public function addAssociations(string $objectType, string $associationType, array $payload): Response\n {\n $endpoint = \"/crm/v4/associations/$objectType/$associationType/batch/create\";\n\n return $this->makeRequest($endpoint, 'POST', $payload);\n }\n\n public function removeAssociations(string $objectType, string $associationType, array $payload): Response\n {\n $endpoint = \"/crm/v4/associations/$objectType/$associationType/batch/archive\";\n\n return $this->makeRequest($endpoint, 'POST', $payload);\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":true,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"19","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"[2026-05-07 14:21:15] local.INFO: [Hubspot] DEBUG Getting headers {\n\"headers\":{\n\"Date\":[\"Thu,07 May 2026 14:21:15 GMT\"],\n \"Content-Type\":[\"application/json;charset=utf-8\"],\n \"Transfer-Encoding\":[\"chunked\"],\n \"Connection\":[\"keep-alive\"],\n \"CF-Ray\":[\"9f80deb8db60dc3a-SOF\"],\n \"CF-Cache-Status\":[\"DYNAMIC\"],\n \"Strict-Transport-Security\":[\"max-age=31536000; includeSubDomains; preload\"],\n \"Vary\":[\"origin,\n accept-encoding\"],\n \"access-control-allow-credentials\":[\"false\"],\n \"server-timing\":[\"hcid;desc=\\\"019e02d0-6fd8-7812-bdba-885b7ccb3ee3\\\",\n cfr;desc=\\\"9f80deb8e7c6dc3a-IAD\\\"\"],\n \"x-content-type-options\":[\"nosniff\"],\n \"x-hubspot-correlation-id\":[\"019e02d0-6fd8-7812-bdba-885b7ccb3ee3\"],\n \"Set-Cookie\":[\"__cf_bm=SIUrtdQgXVrik50pdqF6hZVYKhzTnQBidvMabeCtm0Y-1778163675-1.0.1.1-rI.ZggtDKxTge5zr8_2gbBfWMQQ.ufZEXDZyHz2mBUFdzdo2gTHEsOkXMSEShjK0hGYxNhUGM1ZoBpX7BcFZcHEjA7Cs_.SMUhUnd2nYjko; path=/; expires=Thu,\n 07-May-26 14:51:15 GMT; domain=.hubapi.com; HttpOnly; Secure; SameSite=None\"],\n \"Report-To\":[\"{\n\\\"endpoints\\\":[{\n\\\"url\\\":\\\"https:\\\\/\\\\/a.nel.cloudflare.com\\\\/report\\\\/v4?s=NYAlsVTP0fYm32qrSDjxYE4sd2RWRqiSp3wHsmdEgZlzoYdxI%2BIxVpHmsKn3O%2BKVA3mFIJ2m7YRECDGSM%2BW2IYTzo6FM4%2BdUIjURO8srzKSvJgZ%2BQ6R79arKQw3uHLlX\\\"}],\n\\\"group\\\":\\\"cf-nel\\\",\n\\\"max_age\\\":604800}\"],\n\"NEL\":[\"{\n\\\"success_fraction\\\":0.01,\n\\\"report_to\\\":\\\"cf-nel\\\",\n\\\"max_age\\\":604800}\"],\n\"Server\":[\"cloudflare\"]}} {\n\"correlation_id\":\"95236535-ec98-4541-b92a-adfa73b69eab\",\n\"trace_id\":\"c7ab8365-903f-46d4-9403-0e5b551e3545\"}","depth":4,"on_screen":true,"value":"[2026-05-07 14:21:15] local.INFO: [Hubspot] DEBUG Getting headers {\n\"headers\":{\n\"Date\":[\"Thu,07 May 2026 14:21:15 GMT\"],\n \"Content-Type\":[\"application/json;charset=utf-8\"],\n \"Transfer-Encoding\":[\"chunked\"],\n \"Connection\":[\"keep-alive\"],\n \"CF-Ray\":[\"9f80deb8db60dc3a-SOF\"],\n \"CF-Cache-Status\":[\"DYNAMIC\"],\n \"Strict-Transport-Security\":[\"max-age=31536000; includeSubDomains; preload\"],\n \"Vary\":[\"origin,\n accept-encoding\"],\n \"access-control-allow-credentials\":[\"false\"],\n \"server-timing\":[\"hcid;desc=\\\"019e02d0-6fd8-7812-bdba-885b7ccb3ee3\\\",\n cfr;desc=\\\"9f80deb8e7c6dc3a-IAD\\\"\"],\n \"x-content-type-options\":[\"nosniff\"],\n \"x-hubspot-correlation-id\":[\"019e02d0-6fd8-7812-bdba-885b7ccb3ee3\"],\n \"Set-Cookie\":[\"__cf_bm=SIUrtdQgXVrik50pdqF6hZVYKhzTnQBidvMabeCtm0Y-1778163675-1.0.1.1-rI.ZggtDKxTge5zr8_2gbBfWMQQ.ufZEXDZyHz2mBUFdzdo2gTHEsOkXMSEShjK0hGYxNhUGM1ZoBpX7BcFZcHEjA7Cs_.SMUhUnd2nYjko; path=/; expires=Thu,\n 07-May-26 14:51:15 GMT; domain=.hubapi.com; HttpOnly; Secure; SameSite=None\"],\n \"Report-To\":[\"{\n\\\"endpoints\\\":[{\n\\\"url\\\":\\\"https:\\\\/\\\\/a.nel.cloudflare.com\\\\/report\\\\/v4?s=NYAlsVTP0fYm32qrSDjxYE4sd2RWRqiSp3wHsmdEgZlzoYdxI%2BIxVpHmsKn3O%2BKVA3mFIJ2m7YRECDGSM%2BW2IYTzo6FM4%2BdUIjURO8srzKSvJgZ%2BQ6R79arKQw3uHLlX\\\"}],\n\\\"group\\\":\\\"cf-nel\\\",\n\\\"max_age\\\":604800}\"],\n\"NEL\":[\"{\n\\\"success_fraction\\\":0.01,\n\\\"report_to\\\":\\\"cf-nel\\\",\n\\\"max_age\\\":604800}\"],\n\"Server\":[\"cloudflare\"]}} {\n\"correlation_id\":\"95236535-ec98-4541-b92a-adfa73b69eab\",\n\"trace_id\":\"c7ab8365-903f-46d4-9403-0e5b551e3545\"}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
5549352204577814028
|
6378757150172121188
|
idle
|
accessibility
|
NULL
|
Project: faVsco.js, menu
JY-20725-handle-HS-search Project: faVsco.js, menu
JY-20725-handle-HS-search-rate-limit, menu
Start Listening for PHP Debug Connections
HandleHubspotRateLimitTest
Run 'HandleHubspotRateLimitTest'
Debug 'HandleHubspotRateLimitTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
2
67
3
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Services\Crm\Hubspot;
use HubSpot\Client\Crm\Deals\ApiException as DealApiException;
use HubSpot\Client\Crm\Contacts\ApiException as ContactApiException;
use HubSpot\Client\Crm\Companies\ApiException as CompanyApiException;
use HubSpot\Client\Crm\Contacts\Model\SimplePublicObjectWithAssociations as ContactsWithAssociations;
use HubSpot\Client\Crm\Companies\Model\SimplePublicObjectWithAssociations as CompaniesWithAssociations;
use HubSpot\Client\Crm\Deals\Model\SimplePublicObjectWithAssociations as DealWithAssociations;
use HubSpot\Client\Crm\Objects\Model\SimplePublicObjectInput;
use HubSpot\Client\Crm\Objects\Model\SimplePublicObjectWithAssociations as ObjectWithAssociations;
use HubSpot\Client\Crm\Pipelines\Model\Error;
use HubSpot\Client\Crm\Pipelines\Model\PipelineStage;
use HubSpot\Client\Crm\Properties\Model\Property;
use HubSpot\Discovery\Discovery;
use Jiminny\Exceptions\CrmException;
use Jiminny\Exceptions\RateLimitException;
use Jiminny\Exceptions\SocialAccountTokenInvalidException;
use Jiminny\Jobs\Crm\NoteObject;
use Jiminny\Models\Crm\Field;
use Jiminny\Services\Crm\BaseClient;
use Jiminny\Services\Crm\Hubspot\DTO\Response\Owner;
use Jiminny\Services\SocialAccountService;
use SevenShores\Hubspot\Exceptions\BadRequest;
use SevenShores\Hubspot\Exceptions\HubspotException;
use SevenShores\Hubspot\Factory;
use SevenShores\Hubspot\Http\Response;
use Jiminny\Services\Crm\Hubspot\Pagination\HubspotPaginationService;
use Illuminate\Support\Facades\Redis;
use Throwable;
/**
* @phpstan-type CrmFieldOption array{id:string, label:string, value?:string}
*/
class Client extends BaseClient implements HubspotClientInterface
{
public const string MIN_API_VERSION = '2';
public const string BASE_URL = '[URL_WITH_CREDENTIALS] T
* @param callable(): T $apiCall
* @return T
*
* @throws RateLimitException
*/
private function executeRequest(callable $apiCall)
{
$cacheKey = $this->getRateLimitCacheKey();
$cachedRetryAfter = Redis::get($cacheKey);
if (is_string($cachedRetryAfter) && is_numeric($cachedRetryAfter)) {
throw new RateLimitException(
'Hubspot rate limit (cached circuit-breaker)',
(int) $cachedRetryAfter,
);
}
try {
return $apiCall();
} catch (Throwable $e) {
if ($this->isHubspotRateLimit($e)) {
$retryAfter = $this->parseRetryAfter($e);
Redis::setex($cacheKey, $retryAfter, (string) $retryAfter);
$this->log->warning('[Hubspot] Received 429 from API', [
'team_id' => $this->config->team_id,
'config_id' => $this->config->getId(),
'retry_after' => $retryAfter,
'message' => $this->parseMessage($e),
'reason' => $e->getMessage(),
]);
throw new RateLimitException('Hubspot returned 429', $retryAfter, $e);
}
throw $e;
}
}
private function getRateLimitCacheKey(): string
{
return sprintf('hubspot:ratelimit:portal:%d', $this->config->getId());
}
public function isHubspotRateLimit(Throwable $e): bool
{
if ($e instanceof BadRequest
|| $e instanceof DealApiException
|| $e instanceof ContactApiException
|| $e instanceof CompanyApiException
|| $e instanceof \GuzzleHttp\Exception\RequestException
) {
return (int) $e->getCode() === 429;
}
return false;
}
public function parseRetryAfter(Throwable $e): int
{
if (method_exists($e, 'getResponseHeaders')) {
$headers = $e->getResponseHeaders() ?: [];
$value = $headers['Retry-After'] ?? $headers['retry-after'] ?? null;
if (is_array($value)) {
$value = $value[0] ?? null;
}
if (is_numeric($value)) {
return (int) $value;
}
}
$message = strtolower($this->parseMessage($e));
if (str_contains($message, 'daily')) {
return 600;
}
if (str_contains($message, 'ten secondly')) {
return 10;
}
if (str_contains($message, 'secondly')) {
return 1;
}
$this->log->warning('[Hubspot] No retry-after header or known message, using default', [
'exception_class' => get_class($e),
'message' => $message,
]);
return 10;
}
public function parseMessage(Throwable $e): string
{
if (! method_exists($e, 'getResponseBody')) {
return '';
}
$body = $e->getResponseBody();
if (is_string($body)) {
$body = json_decode($body, true) ?? [];
}
if (! is_array($body)) {
return '';
}
return (string) ($body['message'] ?? '');
}
public function getMinimumApiVersion(): string
{
return self::MIN_API_VERSION;
}
public function getInstance(): Factory
{
return new Factory([
'key' => $this->accessToken,
'oauth2' => true,
'base_url' => $this->baseUrl,
]);
}
public function getNewInstance(): Discovery
{
return \HubSpot\Factory::createWithAccessToken($this->accessToken);
}
/**
* Secondly and daily limits for Hubspot API
*
* Product Tier: Free & Starter | Professional & Enterprise | API add-on (any tier)
* Burst: 100/10 seconds | 150/10 seconds | 200/10 seconds
* Daily: 250,000 | 500,000 | 1,000,000
*
* Official documentation states: The search endpoints are rate limited to five requests per second.
* Since with 5 RPS were still hitting secondly rate limits we lowered it to 4
*/
public function getPaginatedData(array $payload, string $type, int $offset = 0): array
{
$total = 0;
$lastId = null;
$rows = [];
foreach ($this->getPaginatedDataGenerator($payload, $type, $offset, $total, $lastId) as $row) {
$rows[] = $row;
}
return ['results' => $rows, 'total' => $total, 'last_record' => $lastId];
}
/**
* @throws HubspotException
* @throws SocialAccountTokenInvalidException
* @throws BadRequest
*/
public function getPaginatedDataGenerator(
array $payload,
string $type,
int $offset = 0,
int &$total = 0,
?string &$lastRecordId = null
): \Generator {
return $this->paginationService->getPaginatedDataGenerator(
$this,
$payload,
$type,
$offset,
$total,
$lastRecordId
);
}
/**
* Execute a search request against HubSpot CRM objects with rate limiting.
*
* @param string $objectType The object type ('deals', 'companies', 'contacts', 'calls')
* @param array<string, mixed> $payload The search payload with filters, sorts, properties, etc.
* @return array The search response with 'results', 'total', 'paging' keys
* @throws RateLimitException When rate limit is hit
* @throws HubspotException On API errors
*/
public function search(string $objectType, array $payload): array
{
$endpoint = self::BASE_URL . "/crm/v3/objects/{$objectType}/search";
return $this->executeRequest(function () use ($endpoint, $payload) {
$response = $this->getInstance()->getClient()->request('POST', $endpoint, ['json' => $payload]);
return $response->toArray();
});
}
/**
* @throws DealApiException
* @throws CrmException
*/
public function getOpportunityById(string $crmId, array $fields): array
{
try {
// $deal = $this->executeRequest(fn () => $this->getNewInstance()->crm()->deals()->basicApi()->getById(
$deal = $this->getNewInstance()->crm()->deals()->basicApi()->getById(
$crmId,
implode(',', $fields),
'companies,contacts'
);
} catch (DealApiException $e) {
$this->log->info('[Hubspot] Failed to fetch opportunity', [
'crm_id' => $crmId,
'reason' => $e->getMessage(),
]);
throw $e;
}
if (! $deal instanceof DealWithAssociations) {
throw new CrmException('Deal not found');
}
return [
'id' => $deal->getId(),
'properties' => $deal->getProperties(),
'associations' => $deal->getAssociations(),
];
}
/**
* Generic batch read method for HubSpot objects
*
* @param string $objectType The object type ('deals', 'companies', 'contacts')
* @param array<string> $crmIds Array of HubSpot object IDs (max 100)
* @param array<string> $fields Array of property names to fetch
*
* @return array<string, array> Array keyed by CRM ID with object data
*/
private function batchReadObjects(string $objectType, array $crmIds, array $fields): array
{
if (empty($crmIds)) {
return [];
}
$this->validateBatchSize($objectType, $crmIds);
$this->ensureValidToken();
try {
$batchConfig = $this->createBatchConfiguration($objectType);
$batchReadRequest = $this->prepareBatchRequest($batchConfig, $crmIds, $fields);
$response = $batchConfig['api']->read($batchReadRequest);
$this->validateApiResponse($response, $objectType);
$results = $this->processApiResults($response);
$this->logBatchResults($objectType, $crmIds, $results);
return $results;
} catch (\Throwable $e) {
$this->handleBatchError($e, $objectType, $crmIds);
}
}
private function validateBatchSize(string $objectType, array $crmIds): void
{
if (count($crmIds) > 100) {
throw new \InvalidArgumentException("Batch size cannot exceed 100 {$objectType}");
}
}
private function createBatchConfiguration(string $objectType): array
{
$configurations = [
'deals' => [
'batchReadRequest' => new \HubSpot\Client\Crm\Deals\Model\BatchReadInputSimplePublicObjectId(),
'inputClass' => \HubSpot\Client\Crm\Deals\Model\SimplePublicObjectId::class,
'api' => $this->getNewInstance()->crm()->deals()->batchApi(),
],
'companies' => [
'batchReadRequest' => new \HubSpot\Client\Crm\Companies\Model\BatchReadInputSimplePublicObjectId(),
'inputClass' => \HubSpot\Client\Crm\Companies\Model\SimplePublicObjectId::class,
'api' => $this->getNewInstance()->crm()->companies()->batchApi(),
],
'contacts' => [
'batchReadRequest' => new \HubSpot\Client\Crm\Contacts\Model\BatchReadInputSimplePublicObjectId(),
'inputClass' => \HubSpot\Client\Crm\Contacts\Model\SimplePublicObjectId::class,
'api' => $this->getNewInstance()->crm()->contacts()->batchApi(),
],
];
if (! isset($configurations[$objectType])) {
throw new \InvalidArgumentException("Unsupported object type: {$objectType}");
}
return $configurations[$objectType];
}
private function prepareBatchRequest(array $batchConfig, array $crmIds, array $fields): object
{
$batchReadRequest = $batchConfig['batchReadRequest'];
$inputClass = $batchConfig['inputClass'];
$inputs = array_map(function ($crmId) use ($inputClass) {
$input = new $inputClass();
$input->setId($crmId);
return $input;
}, $crmIds);
$batchReadRequest->setInputs($inputs);
$batchReadRequest->setProperties($fields);
return $batchReadRequest;
}
private function validateApiResponse($response, string $objectType): void
{
if (! $response) {
throw new CrmException("HubSpot API returned null response for {$objectType} batch read");
}
}
private function processApiResults($response): array
{
$results = [];
$responseResults = $response->getResults();
if ($responseResults) {
foreach ($responseResults as $object) {
if ($object && $object->getId()) {
$results[$object->getId()] = [
'id' => $object->getId(),
'properties' => $object->getProperties() ?: [],
];
}
}
}
return $results;
}
private function logBatchResults(string $objectType, array $crmIds, array $results): void
{
$this->log->info("[HubSpot] Batch fetched {$objectType}", [
'requested_count' => count($crmIds),
'returned_count' => count($results),
'crm_ids' => $crmIds,
]);
}
private function handleBatchError(\Throwable $e, string $objectType, array $crmIds): void
{
$errorMessage = $e->getMessage() ?: 'Unknown error';
$errorTrace = $e->getTraceAsString() ?: 'No trace available';
$this->log->error("[HubSpot] Failed to batch fetch {$objectType}", [
'crm_ids' => $crmIds,
'error' => $errorMessage,
'trace' => $errorTrace,
]);
throw new CrmException("Failed to batch fetch {$objectType}: " . $errorMessage);
}
/**
* Batch read multiple opportunities by their CRM IDs
*
* @param array<string> $crmIds Array of HubSpot deal IDs (max 100)
* @param array<string> $fields Array of property names to fetch
*
* @return array<string, array> Array keyed by CRM ID with opportunity data
*/
public function getOpportunitiesByIds(array $crmIds, array $fields): array
{
return $this->batchReadObjects('deals', $crmIds, $fields);
}
/**
* Batch read multiple companies by their CRM IDs
*
* @param array<string> $crmIds Array of HubSpot company IDs (max 100)
* @param array<string> $fields Array of property names to fetch
*
* @return array<string, array> Array keyed by CRM ID with company data
*/
public function getCompaniesByIds(array $crmIds, array $fields): array
{
return $this->batchReadObjects('companies', $crmIds, $fields);
}
/**
* Batch read multiple contacts by their CRM IDs
*
* @param array<string> $crmIds Array of HubSpot contact IDs (max 100)
* @param array<string> $fields Array of property names to fetch
*
* @return array<string, array> Array keyed by CRM ID with contact data
*/
public function getContactsByIds(array $crmIds, array $fields): array
{
return $this->batchReadObjects('contacts', $crmIds, $fields);
}
/**
* @throws CompanyApiException
* @throws CrmException
*/
public function getAccountById(string $crmId, array $fields): array
{
try {
$company = $this->getNewInstance()->crm()->companies()->basicApi()->getById(
$crmId,
implode(',', $fields),
);
} catch (CompanyApiException $e) {
$this->log->info('[Hubspot] Failed to fetch account', [
'crm_id' => $crmId,
'reason' => $e->getMessage(),
]);
throw $e;
}
if (! $company instanceof CompaniesWithAssociations) {
throw new CrmException('Account not found');
}
return [
'id' => $company->getId(),
'properties' => $company->getProperties(),
];
}
/**
* @throws ContactApiException
* @throws CrmException
*/
public function getContactById(string $crmId, array $fields): array
{
try {
$contact = $this->getNewInstance()->crm()->contacts()->basicApi()->getById(
$crmId,
implode(',', $fields)
);
} catch (ContactApiException $e) {
$this->log->info('[Hubspot] Failed to fetch contact', [
'crm_id' => $crmId,
'reason' => $e->getMessage(),
]);
throw $e;
}
if (! $contact instanceof ContactsWithAssociations) {
throw new CrmException('Contact not found');
}
return [
'id' => $contact->getId(),
'properties' => $contact->getProperties(),
];
}
/**
* This is email search request that Hubspot offers as GET (more generous quota)
*/
public function getContactByEmail(string $email, array $fields = []): array
{
try {
$contact = $this->getNewInstance()->crm()->contacts()->basicApi()->getById(
$email,
implode(',', $fields),
null,
false,
'email'
);
return [
'id' => $contact->getId(),
'properties' => $contact->getProperties(),
];
} catch (ContactApiException $e) {
$this->log->info('[Hubspot] Failed to fetch contact', [
'email' => $email,
'reason' => $e->getMessage(),
]);
return [];
}
}
/**
* @throws CrmException
*/
public function fetchProperty(string $objectType, string $propertyId): Property
{
$result = $this->getNewInstance()->crm()->properties()->coreApi()->getByName($objectType, $propertyId);
if (! $result instanceof Property) {
$this->log->error('[Hubspot] Failed to fetch property', [
'object_type' => $objectType,
'property_id' => $propertyId,
'reason' => $result->getMessage(),
]);
throw new CrmException('Failed to fetch property');
}
return $result;
}
/**
* @return array<CrmFieldOption>
*/
public function fetchPropertyOptions(string $objectType, string $propertyId): array
{
/** @var array<CrmFieldOption> */
return $this->fetchProperty($objectType, $propertyId)->getOptions();
}
/**
* @return array<array{id:string, label:string, deleted:bool}>
*/
public function fetchCallDispositions(): array
{
/** @var Response $response */
$response = $this->getInstance()->engagements()->getCallDispositions();
/**
* @var array<array{
* id:string,
* label:string,
* deleted: bool
* }>
*/
return $response->toArray();
}
/**
* @return array<CrmFieldOption>
*/
public function fetchOpportunityPipelineStages(): array
{
$stages = [];
$apiResponse = $this->getNewInstance()->crm()->pipelines()->pipelinesApi()->getAll('deals');
if ($apiResponse instanceof Error) {
$this->log->error('[Hubspot] Failed to fetch opportunity pipelines', [
'reason' => $apiResponse->getMessage(),
]);
return [];
}
foreach ($apiResponse->getResults() as $pipeline) {
$pipelineStages = array_map(
static function (PipelineStage $stage) {
return [
'id' => $stage->getId(),
'label' => $stage->getLabel(),
];
},
$pipeline->getStages()
);
$stages = array_merge($stages, $pipelineStages);
}
return $stages;
}
public function fetchOpportunityPipelines(): array
{
$pipelines = [];
try {
$apiResponse = $this->makeRequest('/crm/v3/pipelines/deals');
} catch (\Exception $e) {
$this->log->info('[Hubspot] Failed to fetch opportunity pipelines', [
'reason' => $e->getMessage(),
]);
return [];
}
$response = $apiResponse->toArray();
foreach ($response['results'] as $pipeline) {
$pipelines[] = [
'id' => $pipeline['id'],
'label' => $pipeline['label'],
];
}
return $pipelines;
}
/**
* @return array<CrmFieldOption>
*/
public function fetchMeetingOutcomeFieldOptions(Field $field): array
{
return $field->getCrmProviderId() === 'meetingOutcome'
? $this->fetchMeetingOutcomeTypes()
: $this->fetchCallActivityTypes();
}
public function fetchMeetingOutcomeTypes(): array
{
return $this->extractMeetingTypeOptions(
'[URL_WITH_CREDENTIALS] Response $response */
$response = $this->getInstance()
->getClient()
->request('GET', $endpoint);
/**
* @var array<array{
* value: string,
* label: string,
* displayOrder: int
* }> $optionData
*/
$optionData = $response->toArray()['options'] ?? [];
$options = [];
foreach ($optionData as $item) {
$options[] = [
'id' => $item['value'],
'value' => $item['value'],
'label' => $item['label'],
'display_order' => $item['displayOrder'],
];
}
return $options;
}
/**
* @return array<CrmFieldOption>
*/
public function fetchDispositionFieldOptions(): array
{
$options = [];
$dispositions = $this->fetchCallDispositions();
foreach ($dispositions as $disposition) {
if ($disposition['deleted'] !== false) {
continue;
}
$option['value'] = $disposition['id'];
$option['id'] = $disposition['id'];
$option['label'] = $disposition['label'];
$options[] = $option;
}
return $options;
}
/**
* @return array<CrmFieldOption>
*/
public function fetchOpportunityFieldOptions(Field $field): array
{
if ($field->isStageField()) {
return $this->fetchOpportunityPipelineStages();
}
if ($field->isPipelineField()) {
return $this->fetchOpportunityPipelines();
}
return $this->fetchPropertyOptions('deals', $field->getCrmProviderId());
}
/**
* @throws BadRequest
* @throws HubspotException
*/
public function makeRequest(string $endpoint, $method = 'GET', $payload = [], ?string $queryString = null)
{
$endpoint = self::BASE_URL . $endpoint;
if ($method === 'GET') {
return $this->getInstance()->getClient()?->request(
method: $method,
endpoint: $endpoint,
query_string: $queryString
);
} else {
return $this->getInstance()->getClient()->request($method, $endpoint, [
'json' => ($payload),
]);
}
}
/**
* @throws BadRequest
* @throws HubspotException
*/
public function createMeeting(array $payload): Response
{
$endpoint = '/crm/v3/objects/meetings';
return $this->makeRequest($endpoint, 'POST', $payload);
}
/**
* @throws BadRequest
* @throws HubspotException
*/
public function updateMeeting(string $meetingId, array $payload): Response
{
$endpoint = '/crm/v3/objects/meetings/' . $meetingId;
return $this->makeRequest($endpoint, 'PATCH', $payload);
}
/**
* @throws \Exception
*/
public function createNote(
string $body,
string $ownerId,
int $timestamp,
string $objectId,
NoteObject $noteObject
): ?string {
try {
$noteInput = new SimplePublicObjectInput([
'properties' => [
'hs_note_body' => $body,
'hubspot_owner_id' => $ownerId,
'hs_timestamp' => $timestamp,
],
]);
// Create note
$note = $this->getNewInstance()->crm()->objects()->basicApi()->create('note', $noteInput);
$this->getNewInstance()->crm()->objects()->associationsApi()->create(
'note',
$note->getId(),
$this->getNoteObject($noteObject),
$objectId,
$this->getNoteAssociationType($noteObject),
);
return $note->getId();
} catch (\Exception $e) {
$this->log->error('[Hubspot] Failed to create note', [
'objectId' => $objectId,
'noteObject' => $noteObject->getObjectType(),
'reason' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
return null;
}
public function updateEngagement(string $objectId, array $engagement, array $metadata): void
{
$this->getInstance()->engagements()->update($objectId, $engagement, $metadata);
}
public function getEngagementData(string $engagementId): array
{
$engagement = $this->getInstance()->engagements()->get($engagementId);
return $engagement->toArray();
}
public function createEngagement(array $engagement, array $associations, array $metadata): Response
{
return $this->getInstance()
->engagements()
->create($engagement, $associations, $metadata);
}
public function isUnauthorizedException(\Exception $e): bool
{
// Check for specific HubSpot API exception types first
if ($e instanceof BadRequest) {
// BadRequest can contain 401 status codes
return $e->getCode() === 401;
}
// Check for HTTP client exceptions with status codes
if ($e instanceof \GuzzleHttp\Exception\RequestException && $e->hasResponse()) {
$response = $e->getResponse();
if ($response !== null) {
return $response->getStatusCode() === 401;
}
}
// Check for Guzzle HTTP exceptions
if ($e instanceof \GuzzleHttp\Exception\ClientException) {
return $e->getCode() === 401;
}
// Fallback to string matching as last resort, but be more specific
$message = strtolower($e->getMessage());
return str_contains($message, '401 unauthorized') ||
str_contains($message, 'http 401') ||
str_contains($message, 'status code 401') ||
(preg_match('/\b401\b/', $message) && str_contains($message, 'unauthorized'));
}
/**
* Validates and refreshes the access token if needed before API requests.
* This ensures long-running processes don't fail due to token expiration.
*
* @throws SocialAccountTokenInvalidException
*/
public function ensureValidToken(): void
{
if ($this->oauthAccount === null) {
return;
}
$newToken = $this->tokenManager->ensureValidToken($this->oauthAccount);
if ($newToken !== null) {
$this->accessToken = $newToken;
}
}
public function getConfig()
{
return $this->config;
}
// returns only active (archived=false)
public function getOwners(): array
{
return $this->getNewInstance()->crm()->owners()->getAll();
}
/**
* @param bool $archived
*
* @return array<Owner>|[]
*/
public function getOwnersArchived(bool $archived = true): array
{
$endpoint = '/crm/v3/owners';
$queryParams = [
'archived' => $archived ? 'true' : 'false',
];
$queryString = http_build_query($queryParams);
$owners = [];
try {
$response = $this->makeRequest(endpoint: $endpoint, queryString: $queryString);
$responseData = $response?->toArray();
foreach ($responseData['results'] as $result) {
try {
$owners[] = Owner::create($result);
} catch (Throwable $e) {
$this->log->error('[HubSpot] Failed to process owner data', [
'result' => $result,
'error' => $e->getMessage(),
]);
continue;
}
}
} catch (Throwable $e) {
$this->log->error('HubSpot] Failed to fetch owners', [
'archived' => $archived,
'error' => $e->getMessage(),
]);
return [];
}
return $owners;
}
public function getMeeting(string $engagementId): ObjectWithAssociations
{
return $this->getNewInstance()->crm()->objects()->basicApi()
->getById('meeting', $engagementId, null, 'contact,company,deal');
}
public function deleteEngagement(string $engagementId): void
{
$this->getInstance()->engagements()->delete((int) $engagementId);
}
public function getAssociationsData(array $ids, string $fromObject, string $toObject): array
{
$associationData = [];
$idChunks = array_chunk($ids, self::ASSOCIATIONS_BATCH_SIZE_LIMIT);
foreach ($idChunks as $idChunk) {
try {
$batchInput = new \HubSpot\Client\Crm\Associations\Model\BatchInputPublicObjectId();
$batchInput->setInputs(array_map(function ($id) {
$publicObjectId = new \HubSpot\Client\Crm\Associations\Model\PublicObjectId();
$publicObjectId->setId($id);
return $publicObjectId;
}, $idChunk));
$associatedObjectsData = $this
->getNewInstance()
->crm()
->associations()
->batchApi()
->read($fromObject, $toObject, $batchInput);
if ($associatedObjectsData instanceof \HubSpot\Client\Crm\Associations\Model\BatchResponsePublicAssociationMulti) {
foreach ($associatedObjectsData->getResults() as $association) {
$from = $association->getFrom()->getId();
$toAssociations = $association->getTo();
if (! empty($toAssociations)) {
$associationData[$from] = array_map(function ($item) {
return $item->getId();
}, $toAssociations);
}
}
}
// } catch (RateLimitException $e) {
// throw $e;
} catch (\Exception $e) {
$this->log->error('[Hubspot] Failed to fetch associations', [
'from_object' => $fromObject,
'to_object' => $toObject,
'reason' => $e->getMessage(),
]);
}
}
return $associationData;
}
/**
* @throws \Exception
*/
private function getNoteAssociationType(NoteObject $noteObject): string
{
return match($noteObject) {
NoteObject::Opportunity => 'note_to_deal',
NoteObject::Lead, NoteObject::Contact => 'note_to_contact', // or 'note_to_lead' if your portal supports it
NoteObject::Account => 'note_to_company',
NoteObject::Call, NoteObject::Event => throw new \Exception('Not supported'),
};
}
/**
* @throws \Exception
*/
private function getNoteObject(NoteObject $noteObject): string
{
return match($noteObject) {
NoteObject::Opportunity => 'deal',
NoteObject::Lead, NoteObject::Contact => 'contact',
NoteObject::Account => 'company',
NoteObject::Call, NoteObject::Event => throw new \Exception('Not supported'),
};
}
public function addAssociations(string $objectType, string $associationType, array $payload): Response
{
$endpoint = "/crm/v4/associations/$objectType/$associationType/batch/create";
return $this->makeRequest($endpoint, 'POST', $payload);
}
public function removeAssociations(string $objectType, string $associationType, array $payload): Response
{
$endpoint = "/crm/v4/associations/$objectType/$associationType/batch/archive";
return $this->makeRequest($endpoint, 'POST', $payload);
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
19
Previous Highlighted Error
Next Highlighted Error
[2026-05-07 14:21:15] local.INFO: [Hubspot] DEBUG Getting headers {
"headers":{
"Date":["Thu,07 May 2026 14:21:15 GMT"],
"Content-Type":["application/json;charset=utf-8"],
"Transfer-Encoding":["chunked"],
"Connection":["keep-alive"],
"CF-Ray":["9f80deb8db60dc3a-SOF"],
"CF-Cache-Status":["DYNAMIC"],
"Strict-Transport-Security":["max-age=31536000; includeSubDomains; preload"],
"Vary":["origin,
accept-encoding"],
"access-control-allow-credentials":["false"],
"server-timing":["hcid;desc=\"019e02d0-6fd8-7812-bdba-885b7ccb3ee3\",
cfr;desc=\"9f80deb8e7c6dc3a-IAD\""],
"x-content-type-options":["nosniff"],
"x-hubspot-correlation-id":["019e02d0-6fd8-7812-bdba-885b7ccb3ee3"],
"Set-Cookie":["__cf_bm=SIUrtdQgXVrik50pdqF6hZVYKhzTnQBidvMabeCtm0Y-1778163675-[IP_ADDRESS]-rI.ZggtDKxTge5zr8_2gbBfWMQQ.ufZEXDZyHz2mBUFdzdo2gTHEsOkXMSEShjK0hGYxNhUGM1ZoBpX7BcFZcHEjA7Cs_.SMUhUnd2nYjko; path=/; expires=Thu,
07-May-26 14:51:15 GMT; domain=.hubapi.com; HttpOnly; Secure; SameSite=None"],
"Report-To":["{
\"endpoints\":[{
\"url\":\"https:\\/\\/a.nel.cloudflare.com\\/report\\/v4?s=NYAlsVTP0fYm32qrSDjxYE4sd2RWRqiSp3wHsmdEgZlzoYdxI%2BIxVpHmsKn3O%2BKVA3mFIJ2m7YRECDGSM%2BW2IYTzo6FM4%2BdUIjURO8srzKSvJgZ%2BQ6R79arKQw3uHLlX\"}],
\"group\":\"cf-nel\",
\"max_age\":604800}"],
"NEL":["{
\"success_fraction\":0.01,
\"report_to\":\"cf-nel\",
\"max_age\":604800}"],
"Server":["cloudflare"]}} {
"correlation_id":"95236535-ec98-4541-b92a-adfa73b69eab",
"trace_id":"c7ab8365-903f-46d4-9403-0e5b551e3545"}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
18187
|
NULL
|
0
|
2026-05-11T11:03:04.180492+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-11/1778 /Users/lukas/.screenpipe/data/data/2026-05-11/1778497384180_m2.jpg...
|
PhpStorm
|
faVsco.js – Client.php
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
JY-20725-handle-HS-search Project: faVsco.js, menu
JY-20725-handle-HS-search-rate-limit, menu
Start Listening for PHP Debug Connections
HandleHubspotRateLimitTest
Run 'HandleHubspotRateLimitTest'
Debug 'HandleHubspotRateLimitTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
2
67
3
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Services\Crm\Hubspot;
use HubSpot\Client\Crm\Deals\ApiException as DealApiException;
use HubSpot\Client\Crm\Contacts\ApiException as ContactApiException;
use HubSpot\Client\Crm\Companies\ApiException as CompanyApiException;
use HubSpot\Client\Crm\Contacts\Model\SimplePublicObjectWithAssociations as ContactsWithAssociations;
use HubSpot\Client\Crm\Companies\Model\SimplePublicObjectWithAssociations as CompaniesWithAssociations;
use HubSpot\Client\Crm\Deals\Model\SimplePublicObjectWithAssociations as DealWithAssociations;
use HubSpot\Client\Crm\Objects\Model\SimplePublicObjectInput;
use HubSpot\Client\Crm\Objects\Model\SimplePublicObjectWithAssociations as ObjectWithAssociations;
use HubSpot\Client\Crm\Pipelines\Model\Error;
use HubSpot\Client\Crm\Pipelines\Model\PipelineStage;
use HubSpot\Client\Crm\Properties\Model\Property;
use HubSpot\Discovery\Discovery;
use Jiminny\Exceptions\CrmException;
use Jiminny\Exceptions\RateLimitException;
use Jiminny\Exceptions\SocialAccountTokenInvalidException;
use Jiminny\Jobs\Crm\NoteObject;
use Jiminny\Models\Crm\Field;
use Jiminny\Services\Crm\BaseClient;
use Jiminny\Services\Crm\Hubspot\DTO\Response\Owner;
use Jiminny\Services\SocialAccountService;
use SevenShores\Hubspot\Exceptions\BadRequest;
use SevenShores\Hubspot\Exceptions\HubspotException;
use SevenShores\Hubspot\Factory;
use SevenShores\Hubspot\Http\Response;
use Jiminny\Services\Crm\Hubspot\Pagination\HubspotPaginationService;
use Illuminate\Support\Facades\Redis;
use Throwable;
/**
* @phpstan-type CrmFieldOption array{id:string, label:string, value?:string}
*/
class Client extends BaseClient implements HubspotClientInterface
{
public const string MIN_API_VERSION = '2';
public const string BASE_URL = '[URL_WITH_CREDENTIALS] T
* @param callable(): T $apiCall
* @return T
*
* @throws RateLimitException
*/
private function executeRequest(callable $apiCall)
{
$cacheKey = $this->getRateLimitCacheKey();
$cachedRetryAfter = Redis::get($cacheKey);
if (is_string($cachedRetryAfter) && is_numeric($cachedRetryAfter)) {
throw new RateLimitException(
'Hubspot rate limit (cached circuit-breaker)',
(int) $cachedRetryAfter,
);
}
try {
return $apiCall();
} catch (Throwable $e) {
if ($this->isHubspotRateLimit($e)) {
$retryAfter = $this->parseRetryAfter($e);
Redis::setex($cacheKey, $retryAfter, (string) $retryAfter);
$this->log->warning('[Hubspot] Received 429 from API', [
'team_id' => $this->config->team_id,
'config_id' => $this->config->getId(),
'retry_after' => $retryAfter,
'message' => $this->parseMessage($e),
'reason' => $e->getMessage(),
]);
throw new RateLimitException('Hubspot returned 429', $retryAfter, $e);
}
throw $e;
}
}
private function getRateLimitCacheKey(): string
{
return sprintf('hubspot:ratelimit:portal:%d', $this->config->getId());
}
public function isHubspotRateLimit(Throwable $e): bool
{
if ($e instanceof BadRequest
|| $e instanceof DealApiException
|| $e instanceof ContactApiException
|| $e instanceof CompanyApiException
|| $e instanceof \GuzzleHttp\Exception\RequestException
) {
return (int) $e->getCode() === 429;
}
return false;
}
public function parseRetryAfter(Throwable $e): int
{
if (method_exists($e, 'getResponseHeaders')) {
$headers = $e->getResponseHeaders() ?: [];
$value = $headers['Retry-After'] ?? $headers['retry-after'] ?? null;
if (is_array($value)) {
$value = $value[0] ?? null;
}
if (is_numeric($value)) {
return (int) $value;
}
}
$message = strtolower($this->parseMessage($e));
if (str_contains($message, 'daily')) {
return 600;
}
if (str_contains($message, 'ten secondly')) {
return 10;
}
if (str_contains($message, 'secondly')) {
return 1;
}
$this->log->warning('[Hubspot] No retry-after header or known message, using default', [
'exception_class' => get_class($e),
'message' => $message,
]);
return 10;
}
public function parseMessage(Throwable $e): string
{
if (! method_exists($e, 'getResponseBody')) {
return '';
}
$body = $e->getResponseBody();
if (is_string($body)) {
$body = json_decode($body, true) ?? [];
}
if (! is_array($body)) {
return '';
}
return (string) ($body['message'] ?? '');
}
public function getMinimumApiVersion(): string
{
return self::MIN_API_VERSION;
}
public function getInstance(): Factory
{
return new Factory([
'key' => $this->accessToken,
'oauth2' => true,
'base_url' => $this->baseUrl,
]);
}
public function getNewInstance(): Discovery
{
return \HubSpot\Factory::createWithAccessToken($this->accessToken);
}
/**
* Secondly and daily limits for Hubspot API
*
* Product Tier: Free & Starter | Professional & Enterprise | API add-on (any tier)
* Burst: 100/10 seconds | 150/10 seconds | 200/10 seconds
* Daily: 250,000 | 500,000 | 1,000,000
*
* Official documentation states: The search endpoints are rate limited to five requests per second.
* Since with 5 RPS were still hitting secondly rate limits we lowered it to 4
*/
public function getPaginatedData(array $payload, string $type, int $offset = 0): array
{
$total = 0;
$lastId = null;
$rows = [];
foreach ($this->getPaginatedDataGenerator($payload, $type, $offset, $total, $lastId) as $row) {
$rows[] = $row;
}
return ['results' => $rows, 'total' => $total, 'last_record' => $lastId];
}
/**
* @throws HubspotException
* @throws SocialAccountTokenInvalidException
* @throws BadRequest
*/
public function getPaginatedDataGenerator(
array $payload,
string $type,
int $offset = 0,
int &$total = 0,
?string &$lastRecordId = null
): \Generator {
return $this->paginationService->getPaginatedDataGenerator(
$this,
$payload,
$type,
$offset,
$total,
$lastRecordId
);
}
/**
* Execute a search request against HubSpot CRM objects with rate limiting.
*
* @param string $objectType The object type ('deals', 'companies', 'contacts', 'calls')
* @param array<string, mixed> $payload The search payload with filters, sorts, properties, etc.
* @return array The search response with 'results', 'total', 'paging' keys
* @throws RateLimitException When rate limit is hit
* @throws HubspotException On API errors
*/
public function search(string $objectType, array $payload): array
{
$endpoint = self::BASE_URL . "/crm/v3/objects/{$objectType}/search";
return $this->executeRequest(function () use ($endpoint, $payload) {
$response = $this->getInstance()->getClient()->request('POST', $endpoint, ['json' => $payload]);
return $response->toArray();
});
}
/**
* @throws DealApiException
* @throws CrmException
*/
public function getOpportunityById(string $crmId, array $fields): array
{
try {
// $deal = $this->executeRequest(fn () => $this->getNewInstance()->crm()->deals()->basicApi()->getById(
$deal = $this->getNewInstance()->crm()->deals()->basicApi()->getById(
$crmId,
implode(',', $fields),
'companies,contacts'
);
} catch (DealApiException $e) {
$this->log->info('[Hubspot] Failed to fetch opportunity', [
'crm_id' => $crmId,
'reason' => $e->getMessage(),
]);
throw $e;
}
if (! $deal instanceof DealWithAssociations) {
throw new CrmException('Deal not found');
}
return [
'id' => $deal->getId(),
'properties' => $deal->getProperties(),
'associations' => $deal->getAssociations(),
];
}
/**
* Generic batch read method for HubSpot objects
*
* @param string $objectType The object type ('deals', 'companies', 'contacts')
* @param array<string> $crmIds Array of HubSpot object IDs (max 100)
* @param array<string> $fields Array of property names to fetch
*
* @return array<string, array> Array keyed by CRM ID with object data
*/
private function batchReadObjects(string $objectType, array $crmIds, array $fields): array
{
if (empty($crmIds)) {
return [];
}
$this->validateBatchSize($objectType, $crmIds);
$this->ensureValidToken();
try {
$batchConfig = $this->createBatchConfiguration($objectType);
$batchReadRequest = $this->prepareBatchRequest($batchConfig, $crmIds, $fields);
$response = $batchConfig['api']->read($batchReadRequest);
$this->validateApiResponse($response, $objectType);
$results = $this->processApiResults($response);
$this->logBatchResults($objectType, $crmIds, $results);
return $results;
} catch (\Throwable $e) {
$this->handleBatchError($e, $objectType, $crmIds);
}
}
private function validateBatchSize(string $objectType, array $crmIds): void
{
if (count($crmIds) > 100) {
throw new \InvalidArgumentException("Batch size cannot exceed 100 {$objectType}");
}
}
private function createBatchConfiguration(string $objectType): array
{
$configurations = [
'deals' => [
'batchReadRequest' => new \HubSpot\Client\Crm\Deals\Model\BatchReadInputSimplePublicObjectId(),
'inputClass' => \HubSpot\Client\Crm\Deals\Model\SimplePublicObjectId::class,
'api' => $this->getNewInstance()->crm()->deals()->batchApi(),
],
'companies' => [
'batchReadRequest' => new \HubSpot\Client\Crm\Companies\Model\BatchReadInputSimplePublicObjectId(),
'inputClass' => \HubSpot\Client\Crm\Companies\Model\SimplePublicObjectId::class,
'api' => $this->getNewInstance()->crm()->companies()->batchApi(),
],
'contacts' => [
'batchReadRequest' => new \HubSpot\Client\Crm\Contacts\Model\BatchReadInputSimplePublicObjectId(),
'inputClass' => \HubSpot\Client\Crm\Contacts\Model\SimplePublicObjectId::class,
'api' => $this->getNewInstance()->crm()->contacts()->batchApi(),
],
];
if (! isset($configurations[$objectType])) {
throw new \InvalidArgumentException("Unsupported object type: {$objectType}");
}
return $configurations[$objectType];
}
private function prepareBatchRequest(array $batchConfig, array $crmIds, array $fields): object
{
$batchReadRequest = $batchConfig['batchReadRequest'];
$inputClass = $batchConfig['inputClass'];
$inputs = array_map(function ($crmId) use ($inputClass) {
$input = new $inputClass();
$input->setId($crmId);
return $input;
}, $crmIds);
$batchReadRequest->setInputs($inputs);
$batchReadRequest->setProperties($fields);
return $batchReadRequest;
}
private function validateApiResponse($response, string $objectType): void
{
if (! $response) {
throw new CrmException("HubSpot API returned null response for {$objectType} batch read");
}
}
private function processApiResults($response): array
{
$results = [];
$responseResults = $response->getResults();
if ($responseResults) {
foreach ($responseResults as $object) {
if ($object && $object->getId()) {
$results[$object->getId()] = [
'id' => $object->getId(),
'properties' => $object->getProperties() ?: [],
];
}
}
}
return $results;
}
private function logBatchResults(string $objectType, array $crmIds, array $results): void
{
$this->log->info("[HubSpot] Batch fetched {$objectType}", [
'requested_count' => count($crmIds),
'returned_count' => count($results),
'crm_ids' => $crmIds,
]);
}
private function handleBatchError(\Throwable $e, string $objectType, array $crmIds): void
{
$errorMessage = $e->getMessage() ?: 'Unknown error';
$errorTrace = $e->getTraceAsString() ?: 'No trace available';
$this->log->error("[HubSpot] Failed to batch fetch {$objectType}", [
'crm_ids' => $crmIds,
'error' => $errorMessage,
'trace' => $errorTrace,
]);
throw new CrmException("Failed to batch fetch {$objectType}: " . $errorMessage);
}
/**
* Batch read multiple opportunities by their CRM IDs
*
* @param array<string> $crmIds Array of HubSpot deal IDs (max 100)
* @param array<string> $fields Array of property names to fetch
*
* @return array<string, array> Array keyed by CRM ID with opportunity data
*/
public function getOpportunitiesByIds(array $crmIds, array $fields): array
{
return $this->batchReadObjects('deals', $crmIds, $fields);
}
/**
* Batch read multiple companies by their CRM IDs
*
* @param array<string> $crmIds Array of HubSpot company IDs (max 100)
* @param array<string> $fields Array of property names to fetch
*
* @return array<string, array> Array keyed by CRM ID with company data
*/
public function getCompaniesByIds(array $crmIds, array $fields): array
{
return $this->batchReadObjects('companies', $crmIds, $fields);
}
/**
* Batch read multiple contacts by their CRM IDs
*
* @param array<string> $crmIds Array of HubSpot contact IDs (max 100)
* @param array<string> $fields Array of property names to fetch
*
* @return array<string, array> Array keyed by CRM ID with contact data
*/
public function getContactsByIds(array $crmIds, array $fields): array
{
return $this->batchReadObjects('contacts', $crmIds, $fields);
}
/**
* @throws CompanyApiException
* @throws CrmException
*/
public function getAccountById(string $crmId, array $fields): array
{
try {
$company = $this->getNewInstance()->crm()->companies()->basicApi()->getById(
$crmId,
implode(',', $fields),
);
} catch (CompanyApiException $e) {
$this->log->info('[Hubspot] Failed to fetch account', [
'crm_id' => $crmId,
'reason' => $e->getMessage(),
]);
throw $e;
}
if (! $company instanceof CompaniesWithAssociations) {
throw new CrmException('Account not found');
}
return [
'id' => $company->getId(),
'properties' => $company->getProperties(),
];
}
/**
* @throws ContactApiException
* @throws CrmException
*/
public function getContactById(string $crmId, array $fields): array
{
try {
$contact = $this->getNewInstance()->crm()->contacts()->basicApi()->getById(
$crmId,
implode(',', $fields)
);
} catch (ContactApiException $e) {
$this->log->info('[Hubspot] Failed to fetch contact', [
'crm_id' => $crmId,
'reason' => $e->getMessage(),
]);
throw $e;
}
if (! $contact instanceof ContactsWithAssociations) {
throw new CrmException('Contact not found');
}
return [
'id' => $contact->getId(),
'properties' => $contact->getProperties(),
];
}
/**
* This is email search request that Hubspot offers as GET (more generous quota)
*/
public function getContactByEmail(string $email, array $fields = []): array
{
try {
$contact = $this->getNewInstance()->crm()->contacts()->basicApi()->getById(
$email,
implode(',', $fields),
null,
false,
'email'
);
return [
'id' => $contact->getId(),
'properties' => $contact->getProperties(),
];
} catch (ContactApiException $e) {
$this->log->info('[Hubspot] Failed to fetch contact', [
'email' => $email,
'reason' => $e->getMessage(),
]);
return [];
}
}
/**
* @throws CrmException
*/
public function fetchProperty(string $objectType, string $propertyId): Property
{
$result = $this->getNewInstance()->crm()->properties()->coreApi()->getByName($objectType, $propertyId);
if (! $result instanceof Property) {
$this->log->error('[Hubspot] Failed to fetch property', [
'object_type' => $objectType,
'property_id' => $propertyId,
'reason' => $result->getMessage(),
]);
throw new CrmException('Failed to fetch property');
}
return $result;
}
/**
* @return array<CrmFieldOption>
*/
public function fetchPropertyOptions(string $objectType, string $propertyId): array
{
/** @var array<CrmFieldOption> */
return $this->fetchProperty($objectType, $propertyId)->getOptions();
}
/**
* @return array<array{id:string, label:string, deleted:bool}>
*/
public function fetchCallDispositions(): array
{
/** @var Response $response */
$response = $this->getInstance()->engagements()->getCallDispositions();
/**
* @var array<array{
* id:string,
* label:string,
* deleted: bool
* }>
*/
return $response->toArray();
}
/**
* @return array<CrmFieldOption>
*/
public function fetchOpportunityPipelineStages(): array
{
$stages = [];
$apiResponse = $this->getNewInstance()->crm()->pipelines()->pipelinesApi()->getAll('deals');
if ($apiResponse instanceof Error) {
$this->log->error('[Hubspot] Failed to fetch opportunity pipelines', [
'reason' => $apiResponse->getMessage(),
]);
return [];
}
foreach ($apiResponse->getResults() as $pipeline) {
$pipelineStages = array_map(
static function (PipelineStage $stage) {
return [
'id' => $stage->getId(),
'label' => $stage->getLabel(),
];
},
$pipeline->getStages()
);
$stages = array_merge($stages, $pipelineStages);
}
return $stages;
}
public function fetchOpportunityPipelines(): array
{
$pipelines = [];
try {
$apiResponse = $this->makeRequest('/crm/v3/pipelines/deals');
} catch (\Exception $e) {
$this->log->info('[Hubspot] Failed to fetch opportunity pipelines', [
'reason' => $e->getMessage(),
]);
return [];
}
$response = $apiResponse->toArray();
foreach ($response['results'] as $pipeline) {
$pipelines[] = [
'id' => $pipeline['id'],
'label' => $pipeline['label'],
];
}
return $pipelines;
}
/**
* @return array<CrmFieldOption>
*/
public function fetchMeetingOutcomeFieldOptions(Field $field): array
{
return $field->getCrmProviderId() === 'meetingOutcome'
? $this->fetchMeetingOutcomeTypes()
: $this->fetchCallActivityTypes();
}
public function fetchMeetingOutcomeTypes(): array
{
return $this->extractMeetingTypeOptions(
'[URL_WITH_CREDENTIALS] Response $response */
$response = $this->getInstance()
->getClient()
->request('GET', $endpoint);
/**
* @var array<array{
* value: string,
* label: string,
* displayOrder: int
* }> $optionData
*/
$optionData = $response->toArray()['options'] ?? [];
$options = [];
foreach ($optionData as $item) {
$options[] = [
'id' => $item['value'],
'value' => $item['value'],
'label' => $item['label'],
'display_order' => $item['displayOrder'],
];
}
return $options;
}
/**
* @return array<CrmFieldOption>
*/
public function fetchDispositionFieldOptions(): array
{
$options = [];
$dispositions = $this->fetchCallDispositions();
foreach ($dispositions as $disposition) {
if ($disposition['deleted'] !== false) {
continue;
}
$option['value'] = $disposition['id'];
$option['id'] = $disposition['id'];
$option['label'] = $disposition['label'];
$options[] = $option;
}
return $options;
}
/**
* @return array<CrmFieldOption>
*/
public function fetchOpportunityFieldOptions(Field $field): array
{
if ($field->isStageField()) {
return $this->fetchOpportunityPipelineStages();
}
if ($field->isPipelineField()) {
return $this->fetchOpportunityPipelines();
}
return $this->fetchPropertyOptions('deals', $field->getCrmProviderId());
}
/**
* @throws BadRequest
* @throws HubspotException
*/
public function makeRequest(string $endpoint, $method = 'GET', $payload = [], ?string $queryString = null)
{
$endpoint = self::BASE_URL . $endpoint;
if ($method === 'GET') {
return $this->getInstance()->getClient()?->request(
method: $method,
endpoint: $endpoint,
query_string: $queryString
);
} else {
return $this->getInstance()->getClient()->request($method, $endpoint, [
'json' => ($payload),
]);
}
}
/**
* @throws BadRequest
* @throws HubspotException
*/
public function createMeeting(array $payload): Response
{
$endpoint = '/crm/v3/objects/meetings';
return $this->makeRequest($endpoint, 'POST', $payload);
}
/**
* @throws BadRequest
* @throws HubspotException
*/
public function updateMeeting(string $meetingId, array $payload): Response
{
$endpoint = '/crm/v3/objects/meetings/' . $meetingId;
return $this->makeRequest($endpoint, 'PATCH', $payload);
}
/**
* @throws \Exception
*/
public function createNote(
string $body,
string $ownerId,
int $timestamp,
string $objectId,
NoteObject $noteObject
): ?string {
try {
$noteInput = new SimplePublicObjectInput([
'properties' => [
'hs_note_body' => $body,
'hubspot_owner_id' => $ownerId,
'hs_timestamp' => $timestamp,
],
]);
// Create note
$note = $this->getNewInstance()->crm()->objects()->basicApi()->create('note', $noteInput);
$this->getNewInstance()->crm()->objects()->associationsApi()->create(
'note',
$note->getId(),
$this->getNoteObject($noteObject),
$objectId,
$this->getNoteAssociationType($noteObject),
);
return $note->getId();
} catch (\Exception $e) {
$this->log->error('[Hubspot] Failed to create note', [
'objectId' => $objectId,
'noteObject' => $noteObject->getObjectType(),
'reason' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
return null;
}
public function updateEngagement(string $objectId, array $engagement, array $metadata): void
{
$this->getInstance()->engagements()->update($objectId, $engagement, $metadata);
}
public function getEngagementData(string $engagementId): array
{
$engagement = $this->getInstance()->engagements()->get($engagementId);
return $engagement->toArray();
}
public function createEngagement(array $engagement, array $associations, array $metadata): Response
{
return $this->getInstance()
->engagements()
->create($engagement, $associations, $metadata);
}
public function isUnauthorizedException(\Exception $e): bool
{
// Check for specific HubSpot API exception types first
if ($e instanceof BadRequest) {
// BadRequest can contain 401 status codes
return $e->getCode() === 401;
}
// Check for HTTP client exceptions with status codes
if ($e instanceof \GuzzleHttp\Exception\RequestException && $e->hasResponse()) {
$response = $e->getResponse();
if ($response !== null) {
return $response->getStatusCode() === 401;
}
}
// Check for Guzzle HTTP exceptions
if ($e instanceof \GuzzleHttp\Exception\ClientException) {
return $e->getCode() === 401;
}
// Fallback to string matching as last resort, but be more specific
$message = strtolower($e->getMessage());
return str_contains($message, '401 unauthorized') ||
str_contains($message, 'http 401') ||
str_contains($message, 'status code 401') ||
(preg_match('/\b401\b/', $message) && str_contains($message, 'unauthorized'));
}
/**
* Validates and refreshes the access token if needed before API requests.
* This ensures long-running processes don't fail due to token expiration.
*
* @throws SocialAccountTokenInvalidException
*/
public function ensureValidToken(): void
{
if ($this->oauthAccount === null) {
return;
}
$newToken = $this->tokenManager->ensureValidToken($this->oauthAccount);
if ($newToken !== null) {
$this->accessToken = $newToken;
}
}
public function getConfig()
{
return $this->config;
}
// returns only active (archived=false)
public function getOwners(): array
{
return $this->getNewInstance()->crm()->owners()->getAll();
}
/**
* @param bool $archived
*
* @return array<Owner>|[]
*/
public function getOwnersArchived(bool $archived = true): array
{
$endpoint = '/crm/v3/owners';
$queryParams = [
'archived' => $archived ? 'true' : 'false',
];
$queryString = http_build_query($queryParams);
$owners = [];
try {
$response = $this->makeRequest(endpoint: $endpoint, queryString: $queryString);
$responseData = $response?->toArray();
foreach ($responseData['results'] as $result) {
try {
$owners[] = Owner::create($result);
} catch (Throwable $e) {
$this->log->error('[HubSpot] Failed to process owner data', [
'result' => $result,
'error' => $e->getMessage(),
]);
continue;
}
}
} catch (Throwable $e) {
$this->log->error('HubSpot] Failed to fetch owners', [
'archived' => $archived,
'error' => $e->getMessage(),
]);
return [];
}
return $owners;
}
public function getMeeting(string $engagementId): ObjectWithAssociations
{
return $this->getNewInstance()->crm()->objects()->basicApi()
->getById('meeting', $engagementId, null, 'contact,company,deal');
}
public function deleteEngagement(string $engagementId): void
{
$this->getInstance()->engagements()->delete((int) $engagementId);
}
public function getAssociationsData(array $ids, string $fromObject, string $toObject): array
{
$associationData = [];
$idChunks = array_chunk($ids, self::ASSOCIATIONS_BATCH_SIZE_LIMIT);
foreach ($idChunks as $idChunk) {
try {
$batchInput = new \HubSpot\Client\Crm\Associations\Model\BatchInputPublicObjectId();
$batchInput->setInputs(array_map(function ($id) {
$publicObjectId = new \HubSpot\Client\Crm\Associations\Model\PublicObjectId();
$publicObjectId->setId($id);
return $publicObjectId;
}, $idChunk));
$associatedObjectsData = $this
->getNewInstance()
->crm()
->associations()
->batchApi()
->read($fromObject, $toObject, $batchInput);
if ($associatedObjectsData instanceof \HubSpot\Client\Crm\Associations\Model\BatchResponsePublicAssociationMulti) {
foreach ($associatedObjectsData->getResults() as $association) {
$from = $association->getFrom()->getId();
$toAssociations = $association->getTo();
if (! empty($toAssociations)) {
$associationData[$from] = array_map(function ($item) {
return $item->getId();
}, $toAssociations);
}
}
}
// } catch (RateLimitException $e) {
// throw $e;
} catch (\Exception $e) {
$this->log->error('[Hubspot] Failed to fetch associations', [
'from_object' => $fromObject,
'to_object' => $toObject,
'reason' => $e->getMessage(),
]);
}
}
return $associationData;
}
/**
* @throws \Exception
*/
private function getNoteAssociationType(NoteObject $noteObject): string
{
return match($noteObject) {
NoteObject::Opportunity => 'note_to_deal',
NoteObject::Lead, NoteObject::Contact => 'note_to_contact', // or 'note_to_lead' if your portal supports it
NoteObject::Account => 'note_to_company',
NoteObject::Call, NoteObject::Event => throw new \Exception('Not supported'),
};
}
/**
* @throws \Exception
*/
private function getNoteObject(NoteObject $noteObject): string
{
return match($noteObject) {
NoteObject::Opportunity => 'deal',
NoteObject::Lead, NoteObject::Contact => 'contact',
NoteObject::Account => 'company',
NoteObject::Call, NoteObject::Event => throw new \Exception('Not supported'),
};
}
public function addAssociations(string $objectType, string $associationType, array $payload): Response
{
$endpoint = "/crm/v4/associations/$objectType/$associationType/batch/create";
return $this->makeRequest($endpoint, 'POST', $payload);
}
public function removeAssociations(string $objectType, string $associationType, array $payload): Response
{
$endpoint = "/crm/v4/associations/$objectType/$associationType/batch/archive";
return $this->makeRequest($endpoint, 'POST', $payload);
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
19
Previous Highlighted Error
Next Highlighted Error
[2026-05-07 14:21:15] local.INFO: [Hubspot] DEBUG Getting headers {
"headers":{
"Date":["Thu,07 May 2026 14:21:15 GMT"],
"Content-Type":["application/json;charset=utf-8"],
"Transfer-Encoding":["chunked"],
"Connection":["keep-alive"],
"CF-Ray":["9f80deb8db60dc3a-SOF"],
"CF-Cache-Status":["DYNAMIC"],
"Strict-Transport-Security":["max-age=31536000; includeSubDomains; preload"],
"Vary":["origin,
accept-encoding"],
"access-control-allow-credentials":["false"],
"server-timing":["hcid;desc=\"019e02d0-6fd8-7812-bdba-885b7ccb3ee3\",
cfr;desc=\"9f80deb8e7c6dc3a-IAD\""],
"x-content-type-options":["nosniff"],
"x-hubspot-correlation-id":["019e02d0-6fd8-7812-bdba-885b7ccb3ee3"],
"Set-Cookie":["__cf_bm=SIUrtdQgXVrik50pdqF6hZVYKhzTnQBidvMabeCtm0Y-1778163675-[IP_ADDRESS]-rI.ZggtDKxTge5zr8_2gbBfWMQQ.ufZEXDZyHz2mBUFdzdo2gTHEsOkXMSEShjK0hGYxNhUGM1ZoBpX7BcFZcHEjA7Cs_.SMUhUnd2nYjko; path=/; expires=Thu,
07-May-26 14:51:15 GMT; domain=.hubapi.com; HttpOnly; Secure; SameSite=None"],
"Report-To":["{
\"endpoints\":[{
\"url\":\"https:\\/\\/a.nel.cloudflare.com\\/report\\/v4?s=NYAlsVTP0fYm32qrSDjxYE4sd2RWRqiSp3wHsmdEgZlzoYdxI%2BIxVpHmsKn3O%2BKVA3mFIJ2m7YRECDGSM%2BW2IYTzo6FM4%2BdUIjURO8srzKSvJgZ%2BQ6R79arKQw3uHLlX\"}],
\"group\":\"cf-nel\",
\"max_age\":604800}"],
"NEL":["{
\"success_fraction\":0.01,
\"report_to\":\"cf-nel\",
\"max_age\":604800}"],
"Server":["cloudflare"]}} {
"correlation_id":"95236535-ec98-4541-b92a-adfa73b69eab",
"trace_id":"c7ab8365-903f-46d4-9403-0e5b551e3545"}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"bounds":{"left":0.025930852,"top":0.019952115,"width":0.03856383,"height":0.025538707},"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JY-20725-handle-HS-search-rate-limit, menu","depth":5,"bounds":{"left":0.064494684,"top":0.019952115,"width":0.09541223,"height":0.025538707},"on_screen":true,"help_text":"Git Branch: JY-20725-handle-HS-search-rate-limit","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"bounds":{"left":0.82413566,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"HandleHubspotRateLimitTest","depth":6,"bounds":{"left":0.8394282,"top":0.019952115,"width":0.076130316,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'HandleHubspotRateLimitTest'","depth":6,"bounds":{"left":0.9155585,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'HandleHubspotRateLimitTest'","depth":6,"bounds":{"left":0.9268617,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"bounds":{"left":0.9381649,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"bounds":{"left":0.96609044,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"bounds":{"left":0.9773936,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"bounds":{"left":0.9886968,"top":0.019952115,"width":0.011303186,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"2","depth":4,"bounds":{"left":0.41156915,"top":0.19952115,"width":0.007978723,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"67","depth":4,"bounds":{"left":0.42154256,"top":0.19952115,"width":0.009973404,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"3","depth":4,"bounds":{"left":0.43351063,"top":0.19952115,"width":0.007978723,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.4431516,"top":0.19792499,"width":0.00731383,"height":0.018355945},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.4504654,"top":0.19792499,"width":0.006981383,"height":0.018355945},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Services\\Crm\\Hubspot;\n\nuse HubSpot\\Client\\Crm\\Deals\\ApiException as DealApiException;\nuse HubSpot\\Client\\Crm\\Contacts\\ApiException as ContactApiException;\nuse HubSpot\\Client\\Crm\\Companies\\ApiException as CompanyApiException;\nuse HubSpot\\Client\\Crm\\Contacts\\Model\\SimplePublicObjectWithAssociations as ContactsWithAssociations;\nuse HubSpot\\Client\\Crm\\Companies\\Model\\SimplePublicObjectWithAssociations as CompaniesWithAssociations;\nuse HubSpot\\Client\\Crm\\Deals\\Model\\SimplePublicObjectWithAssociations as DealWithAssociations;\nuse HubSpot\\Client\\Crm\\Objects\\Model\\SimplePublicObjectInput;\nuse HubSpot\\Client\\Crm\\Objects\\Model\\SimplePublicObjectWithAssociations as ObjectWithAssociations;\nuse HubSpot\\Client\\Crm\\Pipelines\\Model\\Error;\nuse HubSpot\\Client\\Crm\\Pipelines\\Model\\PipelineStage;\nuse HubSpot\\Client\\Crm\\Properties\\Model\\Property;\nuse HubSpot\\Discovery\\Discovery;\nuse Jiminny\\Exceptions\\CrmException;\nuse Jiminny\\Exceptions\\RateLimitException;\nuse Jiminny\\Exceptions\\SocialAccountTokenInvalidException;\nuse Jiminny\\Jobs\\Crm\\NoteObject;\nuse Jiminny\\Models\\Crm\\Field;\nuse Jiminny\\Services\\Crm\\BaseClient;\nuse Jiminny\\Services\\Crm\\Hubspot\\DTO\\Response\\Owner;\nuse Jiminny\\Services\\SocialAccountService;\nuse SevenShores\\Hubspot\\Exceptions\\BadRequest;\nuse SevenShores\\Hubspot\\Exceptions\\HubspotException;\nuse SevenShores\\Hubspot\\Factory;\nuse SevenShores\\Hubspot\\Http\\Response;\nuse Jiminny\\Services\\Crm\\Hubspot\\Pagination\\HubspotPaginationService;\nuse Illuminate\\Support\\Facades\\Redis;\nuse Throwable;\n\n/**\n * @phpstan-type CrmFieldOption array{id:string, label:string, value?:string}\n */\nclass Client extends BaseClient implements HubspotClientInterface\n{\n public const string MIN_API_VERSION = '2';\n\n public const string BASE_URL = 'https://api.hubapi.com';\n\n public const int ASSOCIATIONS_BATCH_SIZE_LIMIT = 1000;\n\n private HubspotPaginationService $paginationService;\n private HubspotTokenManager $tokenManager;\n\n public function __construct(\n SocialAccountService $socialAccountService,\n HubspotPaginationService $paginationService,\n HubspotTokenManager $tokenManager\n ) {\n parent::__construct($socialAccountService);\n $this->paginationService = $paginationService;\n $this->tokenManager = $tokenManager;\n\n $this->setBaseUrl(self::BASE_URL);\n $this->setVersion(self::MIN_API_VERSION);\n }\n\n /**\n * Reacts to a rate limits (429) from HubSpot by translating it\n * into a RateLimitException carrying retry_after.\n *\n * Wrap any outbound HubSpot call (SDK or raw HTTP) like:\n *\n * $this->executeRequest(fn () => $this->getNewInstance()->crm()->...);\n *\n * @template T\n * @param callable(): T $apiCall\n * @return T\n *\n * @throws RateLimitException\n */\n private function executeRequest(callable $apiCall)\n {\n $cacheKey = $this->getRateLimitCacheKey();\n\n $cachedRetryAfter = Redis::get($cacheKey);\n if (is_string($cachedRetryAfter) && is_numeric($cachedRetryAfter)) {\n throw new RateLimitException(\n 'Hubspot rate limit (cached circuit-breaker)',\n (int) $cachedRetryAfter,\n );\n }\n\n try {\n return $apiCall();\n } catch (Throwable $e) {\n if ($this->isHubspotRateLimit($e)) {\n $retryAfter = $this->parseRetryAfter($e);\n\n Redis::setex($cacheKey, $retryAfter, (string) $retryAfter);\n\n $this->log->warning('[Hubspot] Received 429 from API', [\n 'team_id' => $this->config->team_id,\n 'config_id' => $this->config->getId(),\n 'retry_after' => $retryAfter,\n 'message' => $this->parseMessage($e),\n 'reason' => $e->getMessage(),\n ]);\n\n throw new RateLimitException('Hubspot returned 429', $retryAfter, $e);\n }\n\n throw $e;\n }\n }\n\n private function getRateLimitCacheKey(): string\n {\n return sprintf('hubspot:ratelimit:portal:%d', $this->config->getId());\n }\n\n public function isHubspotRateLimit(Throwable $e): bool\n {\n if ($e instanceof BadRequest\n || $e instanceof DealApiException\n || $e instanceof ContactApiException\n || $e instanceof CompanyApiException\n || $e instanceof \\GuzzleHttp\\Exception\\RequestException\n ) {\n return (int) $e->getCode() === 429;\n }\n\n return false;\n }\n\n public function parseRetryAfter(Throwable $e): int\n {\n if (method_exists($e, 'getResponseHeaders')) {\n $headers = $e->getResponseHeaders() ?: [];\n $value = $headers['Retry-After'] ?? $headers['retry-after'] ?? null;\n if (is_array($value)) {\n $value = $value[0] ?? null;\n }\n if (is_numeric($value)) {\n return (int) $value;\n }\n }\n\n $message = strtolower($this->parseMessage($e));\n\n if (str_contains($message, 'daily')) {\n return 600;\n }\n if (str_contains($message, 'ten secondly')) {\n return 10;\n }\n if (str_contains($message, 'secondly')) {\n return 1;\n }\n\n $this->log->warning('[Hubspot] No retry-after header or known message, using default', [\n 'exception_class' => get_class($e),\n 'message' => $message,\n ]);\n\n return 10;\n }\n\n public function parseMessage(Throwable $e): string\n {\n if (! method_exists($e, 'getResponseBody')) {\n return '';\n }\n\n $body = $e->getResponseBody();\n if (is_string($body)) {\n $body = json_decode($body, true) ?? [];\n }\n\n if (! is_array($body)) {\n return '';\n }\n\n return (string) ($body['message'] ?? '');\n }\n\n public function getMinimumApiVersion(): string\n {\n return self::MIN_API_VERSION;\n }\n\n public function getInstance(): Factory\n {\n return new Factory([\n 'key' => $this->accessToken,\n 'oauth2' => true,\n 'base_url' => $this->baseUrl,\n ]);\n }\n\n public function getNewInstance(): Discovery\n {\n return \\HubSpot\\Factory::createWithAccessToken($this->accessToken);\n }\n\n /**\n * Secondly and daily limits for Hubspot API\n *\n * Product Tier: Free & Starter | Professional & Enterprise | API add-on (any tier)\n * Burst: 100/10 seconds | 150/10 seconds | 200/10 seconds\n * Daily: 250,000 | 500,000 | 1,000,000\n *\n * Official documentation states: The search endpoints are rate limited to five requests per second.\n * Since with 5 RPS were still hitting secondly rate limits we lowered it to 4\n */\n public function getPaginatedData(array $payload, string $type, int $offset = 0): array\n {\n $total = 0;\n $lastId = null;\n $rows = [];\n foreach ($this->getPaginatedDataGenerator($payload, $type, $offset, $total, $lastId) as $row) {\n $rows[] = $row;\n }\n\n return ['results' => $rows, 'total' => $total, 'last_record' => $lastId];\n }\n\n /**\n * @throws HubspotException\n * @throws SocialAccountTokenInvalidException\n * @throws BadRequest\n */\n public function getPaginatedDataGenerator(\n array $payload,\n string $type,\n int $offset = 0,\n int &$total = 0,\n ?string &$lastRecordId = null\n ): \\Generator {\n return $this->paginationService->getPaginatedDataGenerator(\n $this,\n $payload,\n $type,\n $offset,\n $total,\n $lastRecordId\n );\n }\n\n /**\n * Execute a search request against HubSpot CRM objects with rate limiting.\n *\n * @param string $objectType The object type ('deals', 'companies', 'contacts', 'calls')\n * @param array<string, mixed> $payload The search payload with filters, sorts, properties, etc.\n * @return array The search response with 'results', 'total', 'paging' keys\n * @throws RateLimitException When rate limit is hit\n * @throws HubspotException On API errors\n */\n public function search(string $objectType, array $payload): array\n {\n $endpoint = self::BASE_URL . \"/crm/v3/objects/{$objectType}/search\";\n\n return $this->executeRequest(function () use ($endpoint, $payload) {\n $response = $this->getInstance()->getClient()->request('POST', $endpoint, ['json' => $payload]);\n\n return $response->toArray();\n });\n }\n\n /**\n * @throws DealApiException\n * @throws CrmException\n */\n public function getOpportunityById(string $crmId, array $fields): array\n {\n try {\n// $deal = $this->executeRequest(fn () => $this->getNewInstance()->crm()->deals()->basicApi()->getById(\n $deal = $this->getNewInstance()->crm()->deals()->basicApi()->getById(\n $crmId,\n implode(',', $fields),\n 'companies,contacts'\n );\n } catch (DealApiException $e) {\n $this->log->info('[Hubspot] Failed to fetch opportunity', [\n 'crm_id' => $crmId,\n 'reason' => $e->getMessage(),\n ]);\n\n throw $e;\n }\n\n if (! $deal instanceof DealWithAssociations) {\n throw new CrmException('Deal not found');\n }\n\n return [\n 'id' => $deal->getId(),\n 'properties' => $deal->getProperties(),\n 'associations' => $deal->getAssociations(),\n ];\n }\n\n /**\n * Generic batch read method for HubSpot objects\n *\n * @param string $objectType The object type ('deals', 'companies', 'contacts')\n * @param array<string> $crmIds Array of HubSpot object IDs (max 100)\n * @param array<string> $fields Array of property names to fetch\n *\n * @return array<string, array> Array keyed by CRM ID with object data\n */\n private function batchReadObjects(string $objectType, array $crmIds, array $fields): array\n {\n if (empty($crmIds)) {\n return [];\n }\n\n $this->validateBatchSize($objectType, $crmIds);\n $this->ensureValidToken();\n\n try {\n $batchConfig = $this->createBatchConfiguration($objectType);\n $batchReadRequest = $this->prepareBatchRequest($batchConfig, $crmIds, $fields);\n $response = $batchConfig['api']->read($batchReadRequest);\n\n $this->validateApiResponse($response, $objectType);\n\n $results = $this->processApiResults($response);\n $this->logBatchResults($objectType, $crmIds, $results);\n\n return $results;\n } catch (\\Throwable $e) {\n $this->handleBatchError($e, $objectType, $crmIds);\n }\n }\n\n private function validateBatchSize(string $objectType, array $crmIds): void\n {\n if (count($crmIds) > 100) {\n throw new \\InvalidArgumentException(\"Batch size cannot exceed 100 {$objectType}\");\n }\n }\n\n private function createBatchConfiguration(string $objectType): array\n {\n $configurations = [\n 'deals' => [\n 'batchReadRequest' => new \\HubSpot\\Client\\Crm\\Deals\\Model\\BatchReadInputSimplePublicObjectId(),\n 'inputClass' => \\HubSpot\\Client\\Crm\\Deals\\Model\\SimplePublicObjectId::class,\n 'api' => $this->getNewInstance()->crm()->deals()->batchApi(),\n ],\n 'companies' => [\n 'batchReadRequest' => new \\HubSpot\\Client\\Crm\\Companies\\Model\\BatchReadInputSimplePublicObjectId(),\n 'inputClass' => \\HubSpot\\Client\\Crm\\Companies\\Model\\SimplePublicObjectId::class,\n 'api' => $this->getNewInstance()->crm()->companies()->batchApi(),\n ],\n 'contacts' => [\n 'batchReadRequest' => new \\HubSpot\\Client\\Crm\\Contacts\\Model\\BatchReadInputSimplePublicObjectId(),\n 'inputClass' => \\HubSpot\\Client\\Crm\\Contacts\\Model\\SimplePublicObjectId::class,\n 'api' => $this->getNewInstance()->crm()->contacts()->batchApi(),\n ],\n ];\n\n if (! isset($configurations[$objectType])) {\n throw new \\InvalidArgumentException(\"Unsupported object type: {$objectType}\");\n }\n\n return $configurations[$objectType];\n }\n\n private function prepareBatchRequest(array $batchConfig, array $crmIds, array $fields): object\n {\n $batchReadRequest = $batchConfig['batchReadRequest'];\n $inputClass = $batchConfig['inputClass'];\n\n $inputs = array_map(function ($crmId) use ($inputClass) {\n $input = new $inputClass();\n $input->setId($crmId);\n\n return $input;\n }, $crmIds);\n\n $batchReadRequest->setInputs($inputs);\n $batchReadRequest->setProperties($fields);\n\n return $batchReadRequest;\n }\n\n private function validateApiResponse($response, string $objectType): void\n {\n if (! $response) {\n throw new CrmException(\"HubSpot API returned null response for {$objectType} batch read\");\n }\n }\n\n private function processApiResults($response): array\n {\n $results = [];\n $responseResults = $response->getResults();\n\n if ($responseResults) {\n foreach ($responseResults as $object) {\n if ($object && $object->getId()) {\n $results[$object->getId()] = [\n 'id' => $object->getId(),\n 'properties' => $object->getProperties() ?: [],\n ];\n }\n }\n }\n\n return $results;\n }\n\n private function logBatchResults(string $objectType, array $crmIds, array $results): void\n {\n $this->log->info(\"[HubSpot] Batch fetched {$objectType}\", [\n 'requested_count' => count($crmIds),\n 'returned_count' => count($results),\n 'crm_ids' => $crmIds,\n ]);\n }\n\n private function handleBatchError(\\Throwable $e, string $objectType, array $crmIds): void\n {\n $errorMessage = $e->getMessage() ?: 'Unknown error';\n $errorTrace = $e->getTraceAsString() ?: 'No trace available';\n\n $this->log->error(\"[HubSpot] Failed to batch fetch {$objectType}\", [\n 'crm_ids' => $crmIds,\n 'error' => $errorMessage,\n 'trace' => $errorTrace,\n ]);\n\n throw new CrmException(\"Failed to batch fetch {$objectType}: \" . $errorMessage);\n }\n\n /**\n * Batch read multiple opportunities by their CRM IDs\n *\n * @param array<string> $crmIds Array of HubSpot deal IDs (max 100)\n * @param array<string> $fields Array of property names to fetch\n *\n * @return array<string, array> Array keyed by CRM ID with opportunity data\n */\n public function getOpportunitiesByIds(array $crmIds, array $fields): array\n {\n return $this->batchReadObjects('deals', $crmIds, $fields);\n }\n\n /**\n * Batch read multiple companies by their CRM IDs\n *\n * @param array<string> $crmIds Array of HubSpot company IDs (max 100)\n * @param array<string> $fields Array of property names to fetch\n *\n * @return array<string, array> Array keyed by CRM ID with company data\n */\n public function getCompaniesByIds(array $crmIds, array $fields): array\n {\n return $this->batchReadObjects('companies', $crmIds, $fields);\n }\n\n /**\n * Batch read multiple contacts by their CRM IDs\n *\n * @param array<string> $crmIds Array of HubSpot contact IDs (max 100)\n * @param array<string> $fields Array of property names to fetch\n *\n * @return array<string, array> Array keyed by CRM ID with contact data\n */\n public function getContactsByIds(array $crmIds, array $fields): array\n {\n return $this->batchReadObjects('contacts', $crmIds, $fields);\n }\n\n /**\n * @throws CompanyApiException\n * @throws CrmException\n */\n public function getAccountById(string $crmId, array $fields): array\n {\n try {\n $company = $this->getNewInstance()->crm()->companies()->basicApi()->getById(\n $crmId,\n implode(',', $fields),\n );\n } catch (CompanyApiException $e) {\n $this->log->info('[Hubspot] Failed to fetch account', [\n 'crm_id' => $crmId,\n 'reason' => $e->getMessage(),\n ]);\n\n throw $e;\n }\n\n if (! $company instanceof CompaniesWithAssociations) {\n throw new CrmException('Account not found');\n }\n\n return [\n 'id' => $company->getId(),\n 'properties' => $company->getProperties(),\n ];\n }\n\n /**\n * @throws ContactApiException\n * @throws CrmException\n */\n public function getContactById(string $crmId, array $fields): array\n {\n try {\n $contact = $this->getNewInstance()->crm()->contacts()->basicApi()->getById(\n $crmId,\n implode(',', $fields)\n );\n } catch (ContactApiException $e) {\n $this->log->info('[Hubspot] Failed to fetch contact', [\n 'crm_id' => $crmId,\n 'reason' => $e->getMessage(),\n ]);\n\n throw $e;\n }\n\n if (! $contact instanceof ContactsWithAssociations) {\n throw new CrmException('Contact not found');\n }\n\n return [\n 'id' => $contact->getId(),\n 'properties' => $contact->getProperties(),\n ];\n }\n\n /**\n * This is email search request that Hubspot offers as GET (more generous quota)\n */\n public function getContactByEmail(string $email, array $fields = []): array\n {\n try {\n $contact = $this->getNewInstance()->crm()->contacts()->basicApi()->getById(\n $email,\n implode(',', $fields),\n null,\n false,\n 'email'\n );\n\n return [\n 'id' => $contact->getId(),\n 'properties' => $contact->getProperties(),\n ];\n } catch (ContactApiException $e) {\n $this->log->info('[Hubspot] Failed to fetch contact', [\n 'email' => $email,\n 'reason' => $e->getMessage(),\n ]);\n\n return [];\n }\n }\n\n /**\n * @throws CrmException\n */\n public function fetchProperty(string $objectType, string $propertyId): Property\n {\n $result = $this->getNewInstance()->crm()->properties()->coreApi()->getByName($objectType, $propertyId);\n\n if (! $result instanceof Property) {\n $this->log->error('[Hubspot] Failed to fetch property', [\n 'object_type' => $objectType,\n 'property_id' => $propertyId,\n 'reason' => $result->getMessage(),\n ]);\n\n throw new CrmException('Failed to fetch property');\n }\n\n return $result;\n }\n\n /**\n * @return array<CrmFieldOption>\n */\n public function fetchPropertyOptions(string $objectType, string $propertyId): array\n {\n /** @var array<CrmFieldOption> */\n return $this->fetchProperty($objectType, $propertyId)->getOptions();\n }\n\n /**\n * @return array<array{id:string, label:string, deleted:bool}>\n */\n public function fetchCallDispositions(): array\n {\n /** @var Response $response */\n $response = $this->getInstance()->engagements()->getCallDispositions();\n\n /**\n * @var array<array{\n * id:string,\n * label:string,\n * deleted: bool\n * }>\n */\n return $response->toArray();\n }\n\n /**\n * @return array<CrmFieldOption>\n */\n public function fetchOpportunityPipelineStages(): array\n {\n $stages = [];\n $apiResponse = $this->getNewInstance()->crm()->pipelines()->pipelinesApi()->getAll('deals');\n\n if ($apiResponse instanceof Error) {\n $this->log->error('[Hubspot] Failed to fetch opportunity pipelines', [\n 'reason' => $apiResponse->getMessage(),\n ]);\n\n return [];\n }\n\n foreach ($apiResponse->getResults() as $pipeline) {\n $pipelineStages = array_map(\n static function (PipelineStage $stage) {\n return [\n 'id' => $stage->getId(),\n 'label' => $stage->getLabel(),\n ];\n },\n $pipeline->getStages()\n );\n\n $stages = array_merge($stages, $pipelineStages);\n }\n\n return $stages;\n }\n\n public function fetchOpportunityPipelines(): array\n {\n $pipelines = [];\n\n try {\n $apiResponse = $this->makeRequest('/crm/v3/pipelines/deals');\n } catch (\\Exception $e) {\n $this->log->info('[Hubspot] Failed to fetch opportunity pipelines', [\n 'reason' => $e->getMessage(),\n ]);\n\n return [];\n }\n\n $response = $apiResponse->toArray();\n\n foreach ($response['results'] as $pipeline) {\n $pipelines[] = [\n 'id' => $pipeline['id'],\n 'label' => $pipeline['label'],\n ];\n }\n\n return $pipelines;\n }\n\n /**\n * @return array<CrmFieldOption>\n */\n public function fetchMeetingOutcomeFieldOptions(Field $field): array\n {\n return $field->getCrmProviderId() === 'meetingOutcome'\n ? $this->fetchMeetingOutcomeTypes()\n : $this->fetchCallActivityTypes();\n }\n\n public function fetchMeetingOutcomeTypes(): array\n {\n return $this->extractMeetingTypeOptions(\n 'https://api.hubapi.com/crm/v3/properties/meeting/hs_meeting_outcome'\n );\n }\n\n public function fetchCallActivityTypes(): array\n {\n return $this->extractMeetingTypeOptions(\n 'https://api.hubapi.com/crm/v3/properties/call/hs_activity_type'\n );\n }\n\n private function extractMeetingTypeOptions(string $endpoint): array\n {\n /** @var Response $response */\n $response = $this->getInstance()\n ->getClient()\n ->request('GET', $endpoint);\n\n /**\n * @var array<array{\n * value: string,\n * label: string,\n * displayOrder: int\n * }> $optionData\n */\n $optionData = $response->toArray()['options'] ?? [];\n\n $options = [];\n foreach ($optionData as $item) {\n $options[] = [\n 'id' => $item['value'],\n 'value' => $item['value'],\n 'label' => $item['label'],\n 'display_order' => $item['displayOrder'],\n ];\n }\n\n return $options;\n }\n\n /**\n * @return array<CrmFieldOption>\n */\n public function fetchDispositionFieldOptions(): array\n {\n $options = [];\n\n $dispositions = $this->fetchCallDispositions();\n\n foreach ($dispositions as $disposition) {\n if ($disposition['deleted'] !== false) {\n continue;\n }\n\n $option['value'] = $disposition['id'];\n $option['id'] = $disposition['id'];\n $option['label'] = $disposition['label'];\n\n $options[] = $option;\n }\n\n return $options;\n }\n\n /**\n * @return array<CrmFieldOption>\n */\n public function fetchOpportunityFieldOptions(Field $field): array\n {\n if ($field->isStageField()) {\n return $this->fetchOpportunityPipelineStages();\n }\n\n if ($field->isPipelineField()) {\n return $this->fetchOpportunityPipelines();\n }\n\n return $this->fetchPropertyOptions('deals', $field->getCrmProviderId());\n }\n\n /**\n * @throws BadRequest\n * @throws HubspotException\n */\n public function makeRequest(string $endpoint, $method = 'GET', $payload = [], ?string $queryString = null)\n {\n $endpoint = self::BASE_URL . $endpoint;\n\n if ($method === 'GET') {\n return $this->getInstance()->getClient()?->request(\n method: $method,\n endpoint: $endpoint,\n query_string: $queryString\n );\n } else {\n return $this->getInstance()->getClient()->request($method, $endpoint, [\n 'json' => ($payload),\n ]);\n }\n }\n\n /**\n * @throws BadRequest\n * @throws HubspotException\n */\n public function createMeeting(array $payload): Response\n {\n $endpoint = '/crm/v3/objects/meetings';\n\n return $this->makeRequest($endpoint, 'POST', $payload);\n }\n\n /**\n * @throws BadRequest\n * @throws HubspotException\n */\n public function updateMeeting(string $meetingId, array $payload): Response\n {\n $endpoint = '/crm/v3/objects/meetings/' . $meetingId;\n\n return $this->makeRequest($endpoint, 'PATCH', $payload);\n }\n\n /**\n * @throws \\Exception\n */\n public function createNote(\n string $body,\n string $ownerId,\n int $timestamp,\n string $objectId,\n NoteObject $noteObject\n ): ?string {\n try {\n $noteInput = new SimplePublicObjectInput([\n 'properties' => [\n 'hs_note_body' => $body,\n 'hubspot_owner_id' => $ownerId,\n 'hs_timestamp' => $timestamp,\n ],\n ]);\n\n // Create note\n $note = $this->getNewInstance()->crm()->objects()->basicApi()->create('note', $noteInput);\n\n $this->getNewInstance()->crm()->objects()->associationsApi()->create(\n 'note',\n $note->getId(),\n $this->getNoteObject($noteObject),\n $objectId,\n $this->getNoteAssociationType($noteObject),\n );\n\n return $note->getId();\n } catch (\\Exception $e) {\n $this->log->error('[Hubspot] Failed to create note', [\n 'objectId' => $objectId,\n 'noteObject' => $noteObject->getObjectType(),\n 'reason' => $e->getMessage(),\n ]);\n\n \\Sentry::captureException($e);\n }\n\n return null;\n }\n\n public function updateEngagement(string $objectId, array $engagement, array $metadata): void\n {\n $this->getInstance()->engagements()->update($objectId, $engagement, $metadata);\n }\n\n public function getEngagementData(string $engagementId): array\n {\n $engagement = $this->getInstance()->engagements()->get($engagementId);\n\n return $engagement->toArray();\n }\n\n public function createEngagement(array $engagement, array $associations, array $metadata): Response\n {\n return $this->getInstance()\n ->engagements()\n ->create($engagement, $associations, $metadata);\n }\n\n public function isUnauthorizedException(\\Exception $e): bool\n {\n // Check for specific HubSpot API exception types first\n if ($e instanceof BadRequest) {\n // BadRequest can contain 401 status codes\n return $e->getCode() === 401;\n }\n\n // Check for HTTP client exceptions with status codes\n if ($e instanceof \\GuzzleHttp\\Exception\\RequestException && $e->hasResponse()) {\n $response = $e->getResponse();\n if ($response !== null) {\n return $response->getStatusCode() === 401;\n }\n }\n\n // Check for Guzzle HTTP exceptions\n if ($e instanceof \\GuzzleHttp\\Exception\\ClientException) {\n return $e->getCode() === 401;\n }\n\n // Fallback to string matching as last resort, but be more specific\n $message = strtolower($e->getMessage());\n\n return str_contains($message, '401 unauthorized') ||\n str_contains($message, 'http 401') ||\n str_contains($message, 'status code 401') ||\n (preg_match('/\\b401\\b/', $message) && str_contains($message, 'unauthorized'));\n }\n\n /**\n * Validates and refreshes the access token if needed before API requests.\n * This ensures long-running processes don't fail due to token expiration.\n *\n * @throws SocialAccountTokenInvalidException\n */\n public function ensureValidToken(): void\n {\n if ($this->oauthAccount === null) {\n return;\n }\n\n $newToken = $this->tokenManager->ensureValidToken($this->oauthAccount);\n if ($newToken !== null) {\n $this->accessToken = $newToken;\n }\n }\n\n public function getConfig()\n {\n return $this->config;\n }\n\n // returns only active (archived=false)\n public function getOwners(): array\n {\n return $this->getNewInstance()->crm()->owners()->getAll();\n }\n\n /**\n * @param bool $archived\n *\n * @return array<Owner>|[]\n */\n public function getOwnersArchived(bool $archived = true): array\n {\n $endpoint = '/crm/v3/owners';\n $queryParams = [\n 'archived' => $archived ? 'true' : 'false',\n ];\n $queryString = http_build_query($queryParams);\n\n $owners = [];\n\n try {\n $response = $this->makeRequest(endpoint: $endpoint, queryString: $queryString);\n $responseData = $response?->toArray();\n\n foreach ($responseData['results'] as $result) {\n try {\n $owners[] = Owner::create($result);\n } catch (Throwable $e) {\n $this->log->error('[HubSpot] Failed to process owner data', [\n 'result' => $result,\n 'error' => $e->getMessage(),\n ]);\n\n continue;\n }\n }\n } catch (Throwable $e) {\n $this->log->error('HubSpot] Failed to fetch owners', [\n 'archived' => $archived,\n 'error' => $e->getMessage(),\n ]);\n\n return [];\n }\n\n return $owners;\n }\n\n public function getMeeting(string $engagementId): ObjectWithAssociations\n {\n return $this->getNewInstance()->crm()->objects()->basicApi()\n ->getById('meeting', $engagementId, null, 'contact,company,deal');\n }\n\n public function deleteEngagement(string $engagementId): void\n {\n $this->getInstance()->engagements()->delete((int) $engagementId);\n }\n\n public function getAssociationsData(array $ids, string $fromObject, string $toObject): array\n {\n $associationData = [];\n $idChunks = array_chunk($ids, self::ASSOCIATIONS_BATCH_SIZE_LIMIT);\n\n foreach ($idChunks as $idChunk) {\n try {\n $batchInput = new \\HubSpot\\Client\\Crm\\Associations\\Model\\BatchInputPublicObjectId();\n $batchInput->setInputs(array_map(function ($id) {\n $publicObjectId = new \\HubSpot\\Client\\Crm\\Associations\\Model\\PublicObjectId();\n $publicObjectId->setId($id);\n\n return $publicObjectId;\n }, $idChunk));\n\n $associatedObjectsData = $this\n ->getNewInstance()\n ->crm()\n ->associations()\n ->batchApi()\n ->read($fromObject, $toObject, $batchInput);\n\n if ($associatedObjectsData instanceof \\HubSpot\\Client\\Crm\\Associations\\Model\\BatchResponsePublicAssociationMulti) {\n foreach ($associatedObjectsData->getResults() as $association) {\n $from = $association->getFrom()->getId();\n $toAssociations = $association->getTo();\n\n if (! empty($toAssociations)) {\n $associationData[$from] = array_map(function ($item) {\n return $item->getId();\n }, $toAssociations);\n }\n }\n }\n// } catch (RateLimitException $e) {\n// throw $e;\n } catch (\\Exception $e) {\n $this->log->error('[Hubspot] Failed to fetch associations', [\n 'from_object' => $fromObject,\n 'to_object' => $toObject,\n 'reason' => $e->getMessage(),\n ]);\n }\n }\n\n return $associationData;\n }\n\n /**\n * @throws \\Exception\n */\n private function getNoteAssociationType(NoteObject $noteObject): string\n {\n return match($noteObject) {\n NoteObject::Opportunity => 'note_to_deal',\n NoteObject::Lead, NoteObject::Contact => 'note_to_contact', // or 'note_to_lead' if your portal supports it\n NoteObject::Account => 'note_to_company',\n NoteObject::Call, NoteObject::Event => throw new \\Exception('Not supported'),\n };\n }\n\n /**\n * @throws \\Exception\n */\n private function getNoteObject(NoteObject $noteObject): string\n {\n return match($noteObject) {\n NoteObject::Opportunity => 'deal',\n NoteObject::Lead, NoteObject::Contact => 'contact',\n NoteObject::Account => 'company',\n NoteObject::Call, NoteObject::Event => throw new \\Exception('Not supported'),\n };\n }\n\n public function addAssociations(string $objectType, string $associationType, array $payload): Response\n {\n $endpoint = \"/crm/v4/associations/$objectType/$associationType/batch/create\";\n\n return $this->makeRequest($endpoint, 'POST', $payload);\n }\n\n public function removeAssociations(string $objectType, string $associationType, array $payload): Response\n {\n $endpoint = \"/crm/v4/associations/$objectType/$associationType/batch/archive\";\n\n return $this->makeRequest($endpoint, 'POST', $payload);\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Services\\Crm\\Hubspot;\n\nuse HubSpot\\Client\\Crm\\Deals\\ApiException as DealApiException;\nuse HubSpot\\Client\\Crm\\Contacts\\ApiException as ContactApiException;\nuse HubSpot\\Client\\Crm\\Companies\\ApiException as CompanyApiException;\nuse HubSpot\\Client\\Crm\\Contacts\\Model\\SimplePublicObjectWithAssociations as ContactsWithAssociations;\nuse HubSpot\\Client\\Crm\\Companies\\Model\\SimplePublicObjectWithAssociations as CompaniesWithAssociations;\nuse HubSpot\\Client\\Crm\\Deals\\Model\\SimplePublicObjectWithAssociations as DealWithAssociations;\nuse HubSpot\\Client\\Crm\\Objects\\Model\\SimplePublicObjectInput;\nuse HubSpot\\Client\\Crm\\Objects\\Model\\SimplePublicObjectWithAssociations as ObjectWithAssociations;\nuse HubSpot\\Client\\Crm\\Pipelines\\Model\\Error;\nuse HubSpot\\Client\\Crm\\Pipelines\\Model\\PipelineStage;\nuse HubSpot\\Client\\Crm\\Properties\\Model\\Property;\nuse HubSpot\\Discovery\\Discovery;\nuse Jiminny\\Exceptions\\CrmException;\nuse Jiminny\\Exceptions\\RateLimitException;\nuse Jiminny\\Exceptions\\SocialAccountTokenInvalidException;\nuse Jiminny\\Jobs\\Crm\\NoteObject;\nuse Jiminny\\Models\\Crm\\Field;\nuse Jiminny\\Services\\Crm\\BaseClient;\nuse Jiminny\\Services\\Crm\\Hubspot\\DTO\\Response\\Owner;\nuse Jiminny\\Services\\SocialAccountService;\nuse SevenShores\\Hubspot\\Exceptions\\BadRequest;\nuse SevenShores\\Hubspot\\Exceptions\\HubspotException;\nuse SevenShores\\Hubspot\\Factory;\nuse SevenShores\\Hubspot\\Http\\Response;\nuse Jiminny\\Services\\Crm\\Hubspot\\Pagination\\HubspotPaginationService;\nuse Illuminate\\Support\\Facades\\Redis;\nuse Throwable;\n\n/**\n * @phpstan-type CrmFieldOption array{id:string, label:string, value?:string}\n */\nclass Client extends BaseClient implements HubspotClientInterface\n{\n public const string MIN_API_VERSION = '2';\n\n public const string BASE_URL = 'https://api.hubapi.com';\n\n public const int ASSOCIATIONS_BATCH_SIZE_LIMIT = 1000;\n\n private HubspotPaginationService $paginationService;\n private HubspotTokenManager $tokenManager;\n\n public function __construct(\n SocialAccountService $socialAccountService,\n HubspotPaginationService $paginationService,\n HubspotTokenManager $tokenManager\n ) {\n parent::__construct($socialAccountService);\n $this->paginationService = $paginationService;\n $this->tokenManager = $tokenManager;\n\n $this->setBaseUrl(self::BASE_URL);\n $this->setVersion(self::MIN_API_VERSION);\n }\n\n /**\n * Reacts to a rate limits (429) from HubSpot by translating it\n * into a RateLimitException carrying retry_after.\n *\n * Wrap any outbound HubSpot call (SDK or raw HTTP) like:\n *\n * $this->executeRequest(fn () => $this->getNewInstance()->crm()->...);\n *\n * @template T\n * @param callable(): T $apiCall\n * @return T\n *\n * @throws RateLimitException\n */\n private function executeRequest(callable $apiCall)\n {\n $cacheKey = $this->getRateLimitCacheKey();\n\n $cachedRetryAfter = Redis::get($cacheKey);\n if (is_string($cachedRetryAfter) && is_numeric($cachedRetryAfter)) {\n throw new RateLimitException(\n 'Hubspot rate limit (cached circuit-breaker)',\n (int) $cachedRetryAfter,\n );\n }\n\n try {\n return $apiCall();\n } catch (Throwable $e) {\n if ($this->isHubspotRateLimit($e)) {\n $retryAfter = $this->parseRetryAfter($e);\n\n Redis::setex($cacheKey, $retryAfter, (string) $retryAfter);\n\n $this->log->warning('[Hubspot] Received 429 from API', [\n 'team_id' => $this->config->team_id,\n 'config_id' => $this->config->getId(),\n 'retry_after' => $retryAfter,\n 'message' => $this->parseMessage($e),\n 'reason' => $e->getMessage(),\n ]);\n\n throw new RateLimitException('Hubspot returned 429', $retryAfter, $e);\n }\n\n throw $e;\n }\n }\n\n private function getRateLimitCacheKey(): string\n {\n return sprintf('hubspot:ratelimit:portal:%d', $this->config->getId());\n }\n\n public function isHubspotRateLimit(Throwable $e): bool\n {\n if ($e instanceof BadRequest\n || $e instanceof DealApiException\n || $e instanceof ContactApiException\n || $e instanceof CompanyApiException\n || $e instanceof \\GuzzleHttp\\Exception\\RequestException\n ) {\n return (int) $e->getCode() === 429;\n }\n\n return false;\n }\n\n public function parseRetryAfter(Throwable $e): int\n {\n if (method_exists($e, 'getResponseHeaders')) {\n $headers = $e->getResponseHeaders() ?: [];\n $value = $headers['Retry-After'] ?? $headers['retry-after'] ?? null;\n if (is_array($value)) {\n $value = $value[0] ?? null;\n }\n if (is_numeric($value)) {\n return (int) $value;\n }\n }\n\n $message = strtolower($this->parseMessage($e));\n\n if (str_contains($message, 'daily')) {\n return 600;\n }\n if (str_contains($message, 'ten secondly')) {\n return 10;\n }\n if (str_contains($message, 'secondly')) {\n return 1;\n }\n\n $this->log->warning('[Hubspot] No retry-after header or known message, using default', [\n 'exception_class' => get_class($e),\n 'message' => $message,\n ]);\n\n return 10;\n }\n\n public function parseMessage(Throwable $e): string\n {\n if (! method_exists($e, 'getResponseBody')) {\n return '';\n }\n\n $body = $e->getResponseBody();\n if (is_string($body)) {\n $body = json_decode($body, true) ?? [];\n }\n\n if (! is_array($body)) {\n return '';\n }\n\n return (string) ($body['message'] ?? '');\n }\n\n public function getMinimumApiVersion(): string\n {\n return self::MIN_API_VERSION;\n }\n\n public function getInstance(): Factory\n {\n return new Factory([\n 'key' => $this->accessToken,\n 'oauth2' => true,\n 'base_url' => $this->baseUrl,\n ]);\n }\n\n public function getNewInstance(): Discovery\n {\n return \\HubSpot\\Factory::createWithAccessToken($this->accessToken);\n }\n\n /**\n * Secondly and daily limits for Hubspot API\n *\n * Product Tier: Free & Starter | Professional & Enterprise | API add-on (any tier)\n * Burst: 100/10 seconds | 150/10 seconds | 200/10 seconds\n * Daily: 250,000 | 500,000 | 1,000,000\n *\n * Official documentation states: The search endpoints are rate limited to five requests per second.\n * Since with 5 RPS were still hitting secondly rate limits we lowered it to 4\n */\n public function getPaginatedData(array $payload, string $type, int $offset = 0): array\n {\n $total = 0;\n $lastId = null;\n $rows = [];\n foreach ($this->getPaginatedDataGenerator($payload, $type, $offset, $total, $lastId) as $row) {\n $rows[] = $row;\n }\n\n return ['results' => $rows, 'total' => $total, 'last_record' => $lastId];\n }\n\n /**\n * @throws HubspotException\n * @throws SocialAccountTokenInvalidException\n * @throws BadRequest\n */\n public function getPaginatedDataGenerator(\n array $payload,\n string $type,\n int $offset = 0,\n int &$total = 0,\n ?string &$lastRecordId = null\n ): \\Generator {\n return $this->paginationService->getPaginatedDataGenerator(\n $this,\n $payload,\n $type,\n $offset,\n $total,\n $lastRecordId\n );\n }\n\n /**\n * Execute a search request against HubSpot CRM objects with rate limiting.\n *\n * @param string $objectType The object type ('deals', 'companies', 'contacts', 'calls')\n * @param array<string, mixed> $payload The search payload with filters, sorts, properties, etc.\n * @return array The search response with 'results', 'total', 'paging' keys\n * @throws RateLimitException When rate limit is hit\n * @throws HubspotException On API errors\n */\n public function search(string $objectType, array $payload): array\n {\n $endpoint = self::BASE_URL . \"/crm/v3/objects/{$objectType}/search\";\n\n return $this->executeRequest(function () use ($endpoint, $payload) {\n $response = $this->getInstance()->getClient()->request('POST', $endpoint, ['json' => $payload]);\n\n return $response->toArray();\n });\n }\n\n /**\n * @throws DealApiException\n * @throws CrmException\n */\n public function getOpportunityById(string $crmId, array $fields): array\n {\n try {\n// $deal = $this->executeRequest(fn () => $this->getNewInstance()->crm()->deals()->basicApi()->getById(\n $deal = $this->getNewInstance()->crm()->deals()->basicApi()->getById(\n $crmId,\n implode(',', $fields),\n 'companies,contacts'\n );\n } catch (DealApiException $e) {\n $this->log->info('[Hubspot] Failed to fetch opportunity', [\n 'crm_id' => $crmId,\n 'reason' => $e->getMessage(),\n ]);\n\n throw $e;\n }\n\n if (! $deal instanceof DealWithAssociations) {\n throw new CrmException('Deal not found');\n }\n\n return [\n 'id' => $deal->getId(),\n 'properties' => $deal->getProperties(),\n 'associations' => $deal->getAssociations(),\n ];\n }\n\n /**\n * Generic batch read method for HubSpot objects\n *\n * @param string $objectType The object type ('deals', 'companies', 'contacts')\n * @param array<string> $crmIds Array of HubSpot object IDs (max 100)\n * @param array<string> $fields Array of property names to fetch\n *\n * @return array<string, array> Array keyed by CRM ID with object data\n */\n private function batchReadObjects(string $objectType, array $crmIds, array $fields): array\n {\n if (empty($crmIds)) {\n return [];\n }\n\n $this->validateBatchSize($objectType, $crmIds);\n $this->ensureValidToken();\n\n try {\n $batchConfig = $this->createBatchConfiguration($objectType);\n $batchReadRequest = $this->prepareBatchRequest($batchConfig, $crmIds, $fields);\n $response = $batchConfig['api']->read($batchReadRequest);\n\n $this->validateApiResponse($response, $objectType);\n\n $results = $this->processApiResults($response);\n $this->logBatchResults($objectType, $crmIds, $results);\n\n return $results;\n } catch (\\Throwable $e) {\n $this->handleBatchError($e, $objectType, $crmIds);\n }\n }\n\n private function validateBatchSize(string $objectType, array $crmIds): void\n {\n if (count($crmIds) > 100) {\n throw new \\InvalidArgumentException(\"Batch size cannot exceed 100 {$objectType}\");\n }\n }\n\n private function createBatchConfiguration(string $objectType): array\n {\n $configurations = [\n 'deals' => [\n 'batchReadRequest' => new \\HubSpot\\Client\\Crm\\Deals\\Model\\BatchReadInputSimplePublicObjectId(),\n 'inputClass' => \\HubSpot\\Client\\Crm\\Deals\\Model\\SimplePublicObjectId::class,\n 'api' => $this->getNewInstance()->crm()->deals()->batchApi(),\n ],\n 'companies' => [\n 'batchReadRequest' => new \\HubSpot\\Client\\Crm\\Companies\\Model\\BatchReadInputSimplePublicObjectId(),\n 'inputClass' => \\HubSpot\\Client\\Crm\\Companies\\Model\\SimplePublicObjectId::class,\n 'api' => $this->getNewInstance()->crm()->companies()->batchApi(),\n ],\n 'contacts' => [\n 'batchReadRequest' => new \\HubSpot\\Client\\Crm\\Contacts\\Model\\BatchReadInputSimplePublicObjectId(),\n 'inputClass' => \\HubSpot\\Client\\Crm\\Contacts\\Model\\SimplePublicObjectId::class,\n 'api' => $this->getNewInstance()->crm()->contacts()->batchApi(),\n ],\n ];\n\n if (! isset($configurations[$objectType])) {\n throw new \\InvalidArgumentException(\"Unsupported object type: {$objectType}\");\n }\n\n return $configurations[$objectType];\n }\n\n private function prepareBatchRequest(array $batchConfig, array $crmIds, array $fields): object\n {\n $batchReadRequest = $batchConfig['batchReadRequest'];\n $inputClass = $batchConfig['inputClass'];\n\n $inputs = array_map(function ($crmId) use ($inputClass) {\n $input = new $inputClass();\n $input->setId($crmId);\n\n return $input;\n }, $crmIds);\n\n $batchReadRequest->setInputs($inputs);\n $batchReadRequest->setProperties($fields);\n\n return $batchReadRequest;\n }\n\n private function validateApiResponse($response, string $objectType): void\n {\n if (! $response) {\n throw new CrmException(\"HubSpot API returned null response for {$objectType} batch read\");\n }\n }\n\n private function processApiResults($response): array\n {\n $results = [];\n $responseResults = $response->getResults();\n\n if ($responseResults) {\n foreach ($responseResults as $object) {\n if ($object && $object->getId()) {\n $results[$object->getId()] = [\n 'id' => $object->getId(),\n 'properties' => $object->getProperties() ?: [],\n ];\n }\n }\n }\n\n return $results;\n }\n\n private function logBatchResults(string $objectType, array $crmIds, array $results): void\n {\n $this->log->info(\"[HubSpot] Batch fetched {$objectType}\", [\n 'requested_count' => count($crmIds),\n 'returned_count' => count($results),\n 'crm_ids' => $crmIds,\n ]);\n }\n\n private function handleBatchError(\\Throwable $e, string $objectType, array $crmIds): void\n {\n $errorMessage = $e->getMessage() ?: 'Unknown error';\n $errorTrace = $e->getTraceAsString() ?: 'No trace available';\n\n $this->log->error(\"[HubSpot] Failed to batch fetch {$objectType}\", [\n 'crm_ids' => $crmIds,\n 'error' => $errorMessage,\n 'trace' => $errorTrace,\n ]);\n\n throw new CrmException(\"Failed to batch fetch {$objectType}: \" . $errorMessage);\n }\n\n /**\n * Batch read multiple opportunities by their CRM IDs\n *\n * @param array<string> $crmIds Array of HubSpot deal IDs (max 100)\n * @param array<string> $fields Array of property names to fetch\n *\n * @return array<string, array> Array keyed by CRM ID with opportunity data\n */\n public function getOpportunitiesByIds(array $crmIds, array $fields): array\n {\n return $this->batchReadObjects('deals', $crmIds, $fields);\n }\n\n /**\n * Batch read multiple companies by their CRM IDs\n *\n * @param array<string> $crmIds Array of HubSpot company IDs (max 100)\n * @param array<string> $fields Array of property names to fetch\n *\n * @return array<string, array> Array keyed by CRM ID with company data\n */\n public function getCompaniesByIds(array $crmIds, array $fields): array\n {\n return $this->batchReadObjects('companies', $crmIds, $fields);\n }\n\n /**\n * Batch read multiple contacts by their CRM IDs\n *\n * @param array<string> $crmIds Array of HubSpot contact IDs (max 100)\n * @param array<string> $fields Array of property names to fetch\n *\n * @return array<string, array> Array keyed by CRM ID with contact data\n */\n public function getContactsByIds(array $crmIds, array $fields): array\n {\n return $this->batchReadObjects('contacts', $crmIds, $fields);\n }\n\n /**\n * @throws CompanyApiException\n * @throws CrmException\n */\n public function getAccountById(string $crmId, array $fields): array\n {\n try {\n $company = $this->getNewInstance()->crm()->companies()->basicApi()->getById(\n $crmId,\n implode(',', $fields),\n );\n } catch (CompanyApiException $e) {\n $this->log->info('[Hubspot] Failed to fetch account', [\n 'crm_id' => $crmId,\n 'reason' => $e->getMessage(),\n ]);\n\n throw $e;\n }\n\n if (! $company instanceof CompaniesWithAssociations) {\n throw new CrmException('Account not found');\n }\n\n return [\n 'id' => $company->getId(),\n 'properties' => $company->getProperties(),\n ];\n }\n\n /**\n * @throws ContactApiException\n * @throws CrmException\n */\n public function getContactById(string $crmId, array $fields): array\n {\n try {\n $contact = $this->getNewInstance()->crm()->contacts()->basicApi()->getById(\n $crmId,\n implode(',', $fields)\n );\n } catch (ContactApiException $e) {\n $this->log->info('[Hubspot] Failed to fetch contact', [\n 'crm_id' => $crmId,\n 'reason' => $e->getMessage(),\n ]);\n\n throw $e;\n }\n\n if (! $contact instanceof ContactsWithAssociations) {\n throw new CrmException('Contact not found');\n }\n\n return [\n 'id' => $contact->getId(),\n 'properties' => $contact->getProperties(),\n ];\n }\n\n /**\n * This is email search request that Hubspot offers as GET (more generous quota)\n */\n public function getContactByEmail(string $email, array $fields = []): array\n {\n try {\n $contact = $this->getNewInstance()->crm()->contacts()->basicApi()->getById(\n $email,\n implode(',', $fields),\n null,\n false,\n 'email'\n );\n\n return [\n 'id' => $contact->getId(),\n 'properties' => $contact->getProperties(),\n ];\n } catch (ContactApiException $e) {\n $this->log->info('[Hubspot] Failed to fetch contact', [\n 'email' => $email,\n 'reason' => $e->getMessage(),\n ]);\n\n return [];\n }\n }\n\n /**\n * @throws CrmException\n */\n public function fetchProperty(string $objectType, string $propertyId): Property\n {\n $result = $this->getNewInstance()->crm()->properties()->coreApi()->getByName($objectType, $propertyId);\n\n if (! $result instanceof Property) {\n $this->log->error('[Hubspot] Failed to fetch property', [\n 'object_type' => $objectType,\n 'property_id' => $propertyId,\n 'reason' => $result->getMessage(),\n ]);\n\n throw new CrmException('Failed to fetch property');\n }\n\n return $result;\n }\n\n /**\n * @return array<CrmFieldOption>\n */\n public function fetchPropertyOptions(string $objectType, string $propertyId): array\n {\n /** @var array<CrmFieldOption> */\n return $this->fetchProperty($objectType, $propertyId)->getOptions();\n }\n\n /**\n * @return array<array{id:string, label:string, deleted:bool}>\n */\n public function fetchCallDispositions(): array\n {\n /** @var Response $response */\n $response = $this->getInstance()->engagements()->getCallDispositions();\n\n /**\n * @var array<array{\n * id:string,\n * label:string,\n * deleted: bool\n * }>\n */\n return $response->toArray();\n }\n\n /**\n * @return array<CrmFieldOption>\n */\n public function fetchOpportunityPipelineStages(): array\n {\n $stages = [];\n $apiResponse = $this->getNewInstance()->crm()->pipelines()->pipelinesApi()->getAll('deals');\n\n if ($apiResponse instanceof Error) {\n $this->log->error('[Hubspot] Failed to fetch opportunity pipelines', [\n 'reason' => $apiResponse->getMessage(),\n ]);\n\n return [];\n }\n\n foreach ($apiResponse->getResults() as $pipeline) {\n $pipelineStages = array_map(\n static function (PipelineStage $stage) {\n return [\n 'id' => $stage->getId(),\n 'label' => $stage->getLabel(),\n ];\n },\n $pipeline->getStages()\n );\n\n $stages = array_merge($stages, $pipelineStages);\n }\n\n return $stages;\n }\n\n public function fetchOpportunityPipelines(): array\n {\n $pipelines = [];\n\n try {\n $apiResponse = $this->makeRequest('/crm/v3/pipelines/deals');\n } catch (\\Exception $e) {\n $this->log->info('[Hubspot] Failed to fetch opportunity pipelines', [\n 'reason' => $e->getMessage(),\n ]);\n\n return [];\n }\n\n $response = $apiResponse->toArray();\n\n foreach ($response['results'] as $pipeline) {\n $pipelines[] = [\n 'id' => $pipeline['id'],\n 'label' => $pipeline['label'],\n ];\n }\n\n return $pipelines;\n }\n\n /**\n * @return array<CrmFieldOption>\n */\n public function fetchMeetingOutcomeFieldOptions(Field $field): array\n {\n return $field->getCrmProviderId() === 'meetingOutcome'\n ? $this->fetchMeetingOutcomeTypes()\n : $this->fetchCallActivityTypes();\n }\n\n public function fetchMeetingOutcomeTypes(): array\n {\n return $this->extractMeetingTypeOptions(\n 'https://api.hubapi.com/crm/v3/properties/meeting/hs_meeting_outcome'\n );\n }\n\n public function fetchCallActivityTypes(): array\n {\n return $this->extractMeetingTypeOptions(\n 'https://api.hubapi.com/crm/v3/properties/call/hs_activity_type'\n );\n }\n\n private function extractMeetingTypeOptions(string $endpoint): array\n {\n /** @var Response $response */\n $response = $this->getInstance()\n ->getClient()\n ->request('GET', $endpoint);\n\n /**\n * @var array<array{\n * value: string,\n * label: string,\n * displayOrder: int\n * }> $optionData\n */\n $optionData = $response->toArray()['options'] ?? [];\n\n $options = [];\n foreach ($optionData as $item) {\n $options[] = [\n 'id' => $item['value'],\n 'value' => $item['value'],\n 'label' => $item['label'],\n 'display_order' => $item['displayOrder'],\n ];\n }\n\n return $options;\n }\n\n /**\n * @return array<CrmFieldOption>\n */\n public function fetchDispositionFieldOptions(): array\n {\n $options = [];\n\n $dispositions = $this->fetchCallDispositions();\n\n foreach ($dispositions as $disposition) {\n if ($disposition['deleted'] !== false) {\n continue;\n }\n\n $option['value'] = $disposition['id'];\n $option['id'] = $disposition['id'];\n $option['label'] = $disposition['label'];\n\n $options[] = $option;\n }\n\n return $options;\n }\n\n /**\n * @return array<CrmFieldOption>\n */\n public function fetchOpportunityFieldOptions(Field $field): array\n {\n if ($field->isStageField()) {\n return $this->fetchOpportunityPipelineStages();\n }\n\n if ($field->isPipelineField()) {\n return $this->fetchOpportunityPipelines();\n }\n\n return $this->fetchPropertyOptions('deals', $field->getCrmProviderId());\n }\n\n /**\n * @throws BadRequest\n * @throws HubspotException\n */\n public function makeRequest(string $endpoint, $method = 'GET', $payload = [], ?string $queryString = null)\n {\n $endpoint = self::BASE_URL . $endpoint;\n\n if ($method === 'GET') {\n return $this->getInstance()->getClient()?->request(\n method: $method,\n endpoint: $endpoint,\n query_string: $queryString\n );\n } else {\n return $this->getInstance()->getClient()->request($method, $endpoint, [\n 'json' => ($payload),\n ]);\n }\n }\n\n /**\n * @throws BadRequest\n * @throws HubspotException\n */\n public function createMeeting(array $payload): Response\n {\n $endpoint = '/crm/v3/objects/meetings';\n\n return $this->makeRequest($endpoint, 'POST', $payload);\n }\n\n /**\n * @throws BadRequest\n * @throws HubspotException\n */\n public function updateMeeting(string $meetingId, array $payload): Response\n {\n $endpoint = '/crm/v3/objects/meetings/' . $meetingId;\n\n return $this->makeRequest($endpoint, 'PATCH', $payload);\n }\n\n /**\n * @throws \\Exception\n */\n public function createNote(\n string $body,\n string $ownerId,\n int $timestamp,\n string $objectId,\n NoteObject $noteObject\n ): ?string {\n try {\n $noteInput = new SimplePublicObjectInput([\n 'properties' => [\n 'hs_note_body' => $body,\n 'hubspot_owner_id' => $ownerId,\n 'hs_timestamp' => $timestamp,\n ],\n ]);\n\n // Create note\n $note = $this->getNewInstance()->crm()->objects()->basicApi()->create('note', $noteInput);\n\n $this->getNewInstance()->crm()->objects()->associationsApi()->create(\n 'note',\n $note->getId(),\n $this->getNoteObject($noteObject),\n $objectId,\n $this->getNoteAssociationType($noteObject),\n );\n\n return $note->getId();\n } catch (\\Exception $e) {\n $this->log->error('[Hubspot] Failed to create note', [\n 'objectId' => $objectId,\n 'noteObject' => $noteObject->getObjectType(),\n 'reason' => $e->getMessage(),\n ]);\n\n \\Sentry::captureException($e);\n }\n\n return null;\n }\n\n public function updateEngagement(string $objectId, array $engagement, array $metadata): void\n {\n $this->getInstance()->engagements()->update($objectId, $engagement, $metadata);\n }\n\n public function getEngagementData(string $engagementId): array\n {\n $engagement = $this->getInstance()->engagements()->get($engagementId);\n\n return $engagement->toArray();\n }\n\n public function createEngagement(array $engagement, array $associations, array $metadata): Response\n {\n return $this->getInstance()\n ->engagements()\n ->create($engagement, $associations, $metadata);\n }\n\n public function isUnauthorizedException(\\Exception $e): bool\n {\n // Check for specific HubSpot API exception types first\n if ($e instanceof BadRequest) {\n // BadRequest can contain 401 status codes\n return $e->getCode() === 401;\n }\n\n // Check for HTTP client exceptions with status codes\n if ($e instanceof \\GuzzleHttp\\Exception\\RequestException && $e->hasResponse()) {\n $response = $e->getResponse();\n if ($response !== null) {\n return $response->getStatusCode() === 401;\n }\n }\n\n // Check for Guzzle HTTP exceptions\n if ($e instanceof \\GuzzleHttp\\Exception\\ClientException) {\n return $e->getCode() === 401;\n }\n\n // Fallback to string matching as last resort, but be more specific\n $message = strtolower($e->getMessage());\n\n return str_contains($message, '401 unauthorized') ||\n str_contains($message, 'http 401') ||\n str_contains($message, 'status code 401') ||\n (preg_match('/\\b401\\b/', $message) && str_contains($message, 'unauthorized'));\n }\n\n /**\n * Validates and refreshes the access token if needed before API requests.\n * This ensures long-running processes don't fail due to token expiration.\n *\n * @throws SocialAccountTokenInvalidException\n */\n public function ensureValidToken(): void\n {\n if ($this->oauthAccount === null) {\n return;\n }\n\n $newToken = $this->tokenManager->ensureValidToken($this->oauthAccount);\n if ($newToken !== null) {\n $this->accessToken = $newToken;\n }\n }\n\n public function getConfig()\n {\n return $this->config;\n }\n\n // returns only active (archived=false)\n public function getOwners(): array\n {\n return $this->getNewInstance()->crm()->owners()->getAll();\n }\n\n /**\n * @param bool $archived\n *\n * @return array<Owner>|[]\n */\n public function getOwnersArchived(bool $archived = true): array\n {\n $endpoint = '/crm/v3/owners';\n $queryParams = [\n 'archived' => $archived ? 'true' : 'false',\n ];\n $queryString = http_build_query($queryParams);\n\n $owners = [];\n\n try {\n $response = $this->makeRequest(endpoint: $endpoint, queryString: $queryString);\n $responseData = $response?->toArray();\n\n foreach ($responseData['results'] as $result) {\n try {\n $owners[] = Owner::create($result);\n } catch (Throwable $e) {\n $this->log->error('[HubSpot] Failed to process owner data', [\n 'result' => $result,\n 'error' => $e->getMessage(),\n ]);\n\n continue;\n }\n }\n } catch (Throwable $e) {\n $this->log->error('HubSpot] Failed to fetch owners', [\n 'archived' => $archived,\n 'error' => $e->getMessage(),\n ]);\n\n return [];\n }\n\n return $owners;\n }\n\n public function getMeeting(string $engagementId): ObjectWithAssociations\n {\n return $this->getNewInstance()->crm()->objects()->basicApi()\n ->getById('meeting', $engagementId, null, 'contact,company,deal');\n }\n\n public function deleteEngagement(string $engagementId): void\n {\n $this->getInstance()->engagements()->delete((int) $engagementId);\n }\n\n public function getAssociationsData(array $ids, string $fromObject, string $toObject): array\n {\n $associationData = [];\n $idChunks = array_chunk($ids, self::ASSOCIATIONS_BATCH_SIZE_LIMIT);\n\n foreach ($idChunks as $idChunk) {\n try {\n $batchInput = new \\HubSpot\\Client\\Crm\\Associations\\Model\\BatchInputPublicObjectId();\n $batchInput->setInputs(array_map(function ($id) {\n $publicObjectId = new \\HubSpot\\Client\\Crm\\Associations\\Model\\PublicObjectId();\n $publicObjectId->setId($id);\n\n return $publicObjectId;\n }, $idChunk));\n\n $associatedObjectsData = $this\n ->getNewInstance()\n ->crm()\n ->associations()\n ->batchApi()\n ->read($fromObject, $toObject, $batchInput);\n\n if ($associatedObjectsData instanceof \\HubSpot\\Client\\Crm\\Associations\\Model\\BatchResponsePublicAssociationMulti) {\n foreach ($associatedObjectsData->getResults() as $association) {\n $from = $association->getFrom()->getId();\n $toAssociations = $association->getTo();\n\n if (! empty($toAssociations)) {\n $associationData[$from] = array_map(function ($item) {\n return $item->getId();\n }, $toAssociations);\n }\n }\n }\n// } catch (RateLimitException $e) {\n// throw $e;\n } catch (\\Exception $e) {\n $this->log->error('[Hubspot] Failed to fetch associations', [\n 'from_object' => $fromObject,\n 'to_object' => $toObject,\n 'reason' => $e->getMessage(),\n ]);\n }\n }\n\n return $associationData;\n }\n\n /**\n * @throws \\Exception\n */\n private function getNoteAssociationType(NoteObject $noteObject): string\n {\n return match($noteObject) {\n NoteObject::Opportunity => 'note_to_deal',\n NoteObject::Lead, NoteObject::Contact => 'note_to_contact', // or 'note_to_lead' if your portal supports it\n NoteObject::Account => 'note_to_company',\n NoteObject::Call, NoteObject::Event => throw new \\Exception('Not supported'),\n };\n }\n\n /**\n * @throws \\Exception\n */\n private function getNoteObject(NoteObject $noteObject): string\n {\n return match($noteObject) {\n NoteObject::Opportunity => 'deal',\n NoteObject::Lead, NoteObject::Contact => 'contact',\n NoteObject::Account => 'company',\n NoteObject::Call, NoteObject::Event => throw new \\Exception('Not supported'),\n };\n }\n\n public function addAssociations(string $objectType, string $associationType, array $payload): Response\n {\n $endpoint = \"/crm/v4/associations/$objectType/$associationType/batch/create\";\n\n return $this->makeRequest($endpoint, 'POST', $payload);\n }\n\n public function removeAssociations(string $objectType, string $associationType, array $payload): Response\n {\n $endpoint = \"/crm/v4/associations/$objectType/$associationType/batch/archive\";\n\n return $this->makeRequest($endpoint, 'POST', $payload);\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":true,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"19","depth":4,"bounds":{"left":0.7124335,"top":0.10055866,"width":0.009640957,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.7237367,"top":0.09896249,"width":0.00731383,"height":0.018355945},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.73105055,"top":0.09896249,"width":0.006981383,"height":0.018355945},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"[2026-05-07 14:21:15] local.INFO: [Hubspot] DEBUG Getting headers {\n\"headers\":{\n\"Date\":[\"Thu,07 May 2026 14:21:15 GMT\"],\n \"Content-Type\":[\"application/json;charset=utf-8\"],\n \"Transfer-Encoding\":[\"chunked\"],\n \"Connection\":[\"keep-alive\"],\n \"CF-Ray\":[\"9f80deb8db60dc3a-SOF\"],\n \"CF-Cache-Status\":[\"DYNAMIC\"],\n \"Strict-Transport-Security\":[\"max-age=31536000; includeSubDomains; preload\"],\n \"Vary\":[\"origin,\n accept-encoding\"],\n \"access-control-allow-credentials\":[\"false\"],\n \"server-timing\":[\"hcid;desc=\\\"019e02d0-6fd8-7812-bdba-885b7ccb3ee3\\\",\n cfr;desc=\\\"9f80deb8e7c6dc3a-IAD\\\"\"],\n \"x-content-type-options\":[\"nosniff\"],\n \"x-hubspot-correlation-id\":[\"019e02d0-6fd8-7812-bdba-885b7ccb3ee3\"],\n \"Set-Cookie\":[\"__cf_bm=SIUrtdQgXVrik50pdqF6hZVYKhzTnQBidvMabeCtm0Y-1778163675-1.0.1.1-rI.ZggtDKxTge5zr8_2gbBfWMQQ.ufZEXDZyHz2mBUFdzdo2gTHEsOkXMSEShjK0hGYxNhUGM1ZoBpX7BcFZcHEjA7Cs_.SMUhUnd2nYjko; path=/; expires=Thu,\n 07-May-26 14:51:15 GMT; domain=.hubapi.com; HttpOnly; Secure; SameSite=None\"],\n \"Report-To\":[\"{\n\\\"endpoints\\\":[{\n\\\"url\\\":\\\"https:\\\\/\\\\/a.nel.cloudflare.com\\\\/report\\\\/v4?s=NYAlsVTP0fYm32qrSDjxYE4sd2RWRqiSp3wHsmdEgZlzoYdxI%2BIxVpHmsKn3O%2BKVA3mFIJ2m7YRECDGSM%2BW2IYTzo6FM4%2BdUIjURO8srzKSvJgZ%2BQ6R79arKQw3uHLlX\\\"}],\n\\\"group\\\":\\\"cf-nel\\\",\n\\\"max_age\\\":604800}\"],\n\"NEL\":[\"{\n\\\"success_fraction\\\":0.01,\n\\\"report_to\\\":\\\"cf-nel\\\",\n\\\"max_age\\\":604800}\"],\n\"Server\":[\"cloudflare\"]}} {\n\"correlation_id\":\"95236535-ec98-4541-b92a-adfa73b69eab\",\n\"trace_id\":\"c7ab8365-903f-46d4-9403-0e5b551e3545\"}","depth":4,"bounds":{"left":0.47672874,"top":0.09736632,"width":0.52327126,"height":0.8818835},"on_screen":true,"lines":[{"char_start":207,"char_count":30,"bounds":{"left":0.47672874,"top":0.0,"width":0.07513298,"height":0.014365523}},{"char_start":237,"char_count":36,"bounds":{"left":0.47672874,"top":0.0,"width":0.09075798,"height":0.014365523}},{"char_start":273,"char_count":32,"bounds":{"left":0.47672874,"top":0.0,"width":0.080119684,"height":0.014365523}},{"char_start":305,"char_count":79,"bounds":{"left":0.47672874,"top":0.0,"width":0.20212767,"height":0.014365523}},{"char_start":384,"char_count":18,"bounds":{"left":0.47672874,"top":0.0,"width":0.043882977,"height":0.014365523}},{"char_start":402,"char_count":21,"bounds":{"left":0.47672874,"top":0.0,"width":0.051861703,"height":0.014365523}},{"char_start":423,"char_count":48,"bounds":{"left":0.47672874,"top":0.008778931,"width":0.12167553,"height":0.014365523}},{"char_start":471,"char_count":72,"bounds":{"left":0.47672874,"top":0.026336791,"width":0.18384309,"height":0.014365523}},{"char_start":543,"char_count":40,"bounds":{"left":0.47672874,"top":0.043894652,"width":0.10106383,"height":0.014365523}},{"char_start":583,"char_count":41,"bounds":{"left":0.47672874,"top":0.061452515,"width":0.10372341,"height":0.014365523}},{"char_start":624,"char_count":72,"bounds":{"left":0.47672874,"top":0.079010375,"width":0.18384309,"height":0.014365523}},{"char_start":696,"char_count":219,"bounds":{"left":0.47672874,"top":0.096568234,"width":0.52327126,"height":0.014365523}},{"char_start":915,"char_count":83,"bounds":{"left":0.47672874,"top":0.11412609,"width":0.21243352,"height":0.014365523}},{"char_start":998,"char_count":20,"bounds":{"left":0.47672874,"top":0.13168396,"width":0.04920213,"height":0.014365523}},{"char_start":1018,"char_count":17,"bounds":{"left":0.47672874,"top":0.14924182,"width":0.041223403,"height":0.014365523}},{"char_start":1035,"char_count":203,"bounds":{"left":0.47672874,"top":0.16679968,"width":0.52327126,"height":0.014365523}},{"char_start":1238,"char_count":22,"bounds":{"left":0.47672874,"top":0.18435754,"width":0.05418883,"height":0.014365523}},{"char_start":1260,"char_count":23,"bounds":{"left":0.47672874,"top":0.2019154,"width":0.056848403,"height":0.014365523}},{"char_start":1283,"char_count":10,"bounds":{"left":0.47672874,"top":0.21947326,"width":0.023271276,"height":0.014365523}},{"char_start":1293,"char_count":27,"bounds":{"left":0.47672874,"top":0.23703113,"width":0.06715426,"height":0.014365523}},{"char_start":1320,"char_count":26,"bounds":{"left":0.47672874,"top":0.254589,"width":0.06482713,"height":0.014365523}},{"char_start":1346,"char_count":23,"bounds":{"left":0.47672874,"top":0.27214685,"width":0.056848403,"height":0.014365523}},{"char_start":1369,"char_count":28,"bounds":{"left":0.47672874,"top":0.2897047,"width":0.06981383,"height":0.014365523}},{"char_start":1397,"char_count":57,"bounds":{"left":0.47672874,"top":0.30726257,"width":0.14494681,"height":0.014365523}}],"value":"[2026-05-07 14:21:15] local.INFO: [Hubspot] DEBUG Getting headers {\n\"headers\":{\n\"Date\":[\"Thu,07 May 2026 14:21:15 GMT\"],\n \"Content-Type\":[\"application/json;charset=utf-8\"],\n \"Transfer-Encoding\":[\"chunked\"],\n \"Connection\":[\"keep-alive\"],\n \"CF-Ray\":[\"9f80deb8db60dc3a-SOF\"],\n \"CF-Cache-Status\":[\"DYNAMIC\"],\n \"Strict-Transport-Security\":[\"max-age=31536000; includeSubDomains; preload\"],\n \"Vary\":[\"origin,\n accept-encoding\"],\n \"access-control-allow-credentials\":[\"false\"],\n \"server-timing\":[\"hcid;desc=\\\"019e02d0-6fd8-7812-bdba-885b7ccb3ee3\\\",\n cfr;desc=\\\"9f80deb8e7c6dc3a-IAD\\\"\"],\n \"x-content-type-options\":[\"nosniff\"],\n \"x-hubspot-correlation-id\":[\"019e02d0-6fd8-7812-bdba-885b7ccb3ee3\"],\n \"Set-Cookie\":[\"__cf_bm=SIUrtdQgXVrik50pdqF6hZVYKhzTnQBidvMabeCtm0Y-1778163675-1.0.1.1-rI.ZggtDKxTge5zr8_2gbBfWMQQ.ufZEXDZyHz2mBUFdzdo2gTHEsOkXMSEShjK0hGYxNhUGM1ZoBpX7BcFZcHEjA7Cs_.SMUhUnd2nYjko; path=/; expires=Thu,\n 07-May-26 14:51:15 GMT; domain=.hubapi.com; HttpOnly; Secure; SameSite=None\"],\n \"Report-To\":[\"{\n\\\"endpoints\\\":[{\n\\\"url\\\":\\\"https:\\\\/\\\\/a.nel.cloudflare.com\\\\/report\\\\/v4?s=NYAlsVTP0fYm32qrSDjxYE4sd2RWRqiSp3wHsmdEgZlzoYdxI%2BIxVpHmsKn3O%2BKVA3mFIJ2m7YRECDGSM%2BW2IYTzo6FM4%2BdUIjURO8srzKSvJgZ%2BQ6R79arKQw3uHLlX\\\"}],\n\\\"group\\\":\\\"cf-nel\\\",\n\\\"max_age\\\":604800}\"],\n\"NEL\":[\"{\n\\\"success_fraction\\\":0.01,\n\\\"report_to\\\":\\\"cf-nel\\\",\n\\\"max_age\\\":604800}\"],\n\"Server\":[\"cloudflare\"]}} {\n\"correlation_id\":\"95236535-ec98-4541-b92a-adfa73b69eab\",\n\"trace_id\":\"c7ab8365-903f-46d4-9403-0e5b551e3545\"}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"bounds":{"left":0.011968086,"top":0.047885075,"width":0.024268618,"height":0.024740623},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
5549352204577814028
|
6378757150172121188
|
idle
|
accessibility
|
NULL
|
Project: faVsco.js, menu
JY-20725-handle-HS-search Project: faVsco.js, menu
JY-20725-handle-HS-search-rate-limit, menu
Start Listening for PHP Debug Connections
HandleHubspotRateLimitTest
Run 'HandleHubspotRateLimitTest'
Debug 'HandleHubspotRateLimitTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
2
67
3
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Services\Crm\Hubspot;
use HubSpot\Client\Crm\Deals\ApiException as DealApiException;
use HubSpot\Client\Crm\Contacts\ApiException as ContactApiException;
use HubSpot\Client\Crm\Companies\ApiException as CompanyApiException;
use HubSpot\Client\Crm\Contacts\Model\SimplePublicObjectWithAssociations as ContactsWithAssociations;
use HubSpot\Client\Crm\Companies\Model\SimplePublicObjectWithAssociations as CompaniesWithAssociations;
use HubSpot\Client\Crm\Deals\Model\SimplePublicObjectWithAssociations as DealWithAssociations;
use HubSpot\Client\Crm\Objects\Model\SimplePublicObjectInput;
use HubSpot\Client\Crm\Objects\Model\SimplePublicObjectWithAssociations as ObjectWithAssociations;
use HubSpot\Client\Crm\Pipelines\Model\Error;
use HubSpot\Client\Crm\Pipelines\Model\PipelineStage;
use HubSpot\Client\Crm\Properties\Model\Property;
use HubSpot\Discovery\Discovery;
use Jiminny\Exceptions\CrmException;
use Jiminny\Exceptions\RateLimitException;
use Jiminny\Exceptions\SocialAccountTokenInvalidException;
use Jiminny\Jobs\Crm\NoteObject;
use Jiminny\Models\Crm\Field;
use Jiminny\Services\Crm\BaseClient;
use Jiminny\Services\Crm\Hubspot\DTO\Response\Owner;
use Jiminny\Services\SocialAccountService;
use SevenShores\Hubspot\Exceptions\BadRequest;
use SevenShores\Hubspot\Exceptions\HubspotException;
use SevenShores\Hubspot\Factory;
use SevenShores\Hubspot\Http\Response;
use Jiminny\Services\Crm\Hubspot\Pagination\HubspotPaginationService;
use Illuminate\Support\Facades\Redis;
use Throwable;
/**
* @phpstan-type CrmFieldOption array{id:string, label:string, value?:string}
*/
class Client extends BaseClient implements HubspotClientInterface
{
public const string MIN_API_VERSION = '2';
public const string BASE_URL = '[URL_WITH_CREDENTIALS] T
* @param callable(): T $apiCall
* @return T
*
* @throws RateLimitException
*/
private function executeRequest(callable $apiCall)
{
$cacheKey = $this->getRateLimitCacheKey();
$cachedRetryAfter = Redis::get($cacheKey);
if (is_string($cachedRetryAfter) && is_numeric($cachedRetryAfter)) {
throw new RateLimitException(
'Hubspot rate limit (cached circuit-breaker)',
(int) $cachedRetryAfter,
);
}
try {
return $apiCall();
} catch (Throwable $e) {
if ($this->isHubspotRateLimit($e)) {
$retryAfter = $this->parseRetryAfter($e);
Redis::setex($cacheKey, $retryAfter, (string) $retryAfter);
$this->log->warning('[Hubspot] Received 429 from API', [
'team_id' => $this->config->team_id,
'config_id' => $this->config->getId(),
'retry_after' => $retryAfter,
'message' => $this->parseMessage($e),
'reason' => $e->getMessage(),
]);
throw new RateLimitException('Hubspot returned 429', $retryAfter, $e);
}
throw $e;
}
}
private function getRateLimitCacheKey(): string
{
return sprintf('hubspot:ratelimit:portal:%d', $this->config->getId());
}
public function isHubspotRateLimit(Throwable $e): bool
{
if ($e instanceof BadRequest
|| $e instanceof DealApiException
|| $e instanceof ContactApiException
|| $e instanceof CompanyApiException
|| $e instanceof \GuzzleHttp\Exception\RequestException
) {
return (int) $e->getCode() === 429;
}
return false;
}
public function parseRetryAfter(Throwable $e): int
{
if (method_exists($e, 'getResponseHeaders')) {
$headers = $e->getResponseHeaders() ?: [];
$value = $headers['Retry-After'] ?? $headers['retry-after'] ?? null;
if (is_array($value)) {
$value = $value[0] ?? null;
}
if (is_numeric($value)) {
return (int) $value;
}
}
$message = strtolower($this->parseMessage($e));
if (str_contains($message, 'daily')) {
return 600;
}
if (str_contains($message, 'ten secondly')) {
return 10;
}
if (str_contains($message, 'secondly')) {
return 1;
}
$this->log->warning('[Hubspot] No retry-after header or known message, using default', [
'exception_class' => get_class($e),
'message' => $message,
]);
return 10;
}
public function parseMessage(Throwable $e): string
{
if (! method_exists($e, 'getResponseBody')) {
return '';
}
$body = $e->getResponseBody();
if (is_string($body)) {
$body = json_decode($body, true) ?? [];
}
if (! is_array($body)) {
return '';
}
return (string) ($body['message'] ?? '');
}
public function getMinimumApiVersion(): string
{
return self::MIN_API_VERSION;
}
public function getInstance(): Factory
{
return new Factory([
'key' => $this->accessToken,
'oauth2' => true,
'base_url' => $this->baseUrl,
]);
}
public function getNewInstance(): Discovery
{
return \HubSpot\Factory::createWithAccessToken($this->accessToken);
}
/**
* Secondly and daily limits for Hubspot API
*
* Product Tier: Free & Starter | Professional & Enterprise | API add-on (any tier)
* Burst: 100/10 seconds | 150/10 seconds | 200/10 seconds
* Daily: 250,000 | 500,000 | 1,000,000
*
* Official documentation states: The search endpoints are rate limited to five requests per second.
* Since with 5 RPS were still hitting secondly rate limits we lowered it to 4
*/
public function getPaginatedData(array $payload, string $type, int $offset = 0): array
{
$total = 0;
$lastId = null;
$rows = [];
foreach ($this->getPaginatedDataGenerator($payload, $type, $offset, $total, $lastId) as $row) {
$rows[] = $row;
}
return ['results' => $rows, 'total' => $total, 'last_record' => $lastId];
}
/**
* @throws HubspotException
* @throws SocialAccountTokenInvalidException
* @throws BadRequest
*/
public function getPaginatedDataGenerator(
array $payload,
string $type,
int $offset = 0,
int &$total = 0,
?string &$lastRecordId = null
): \Generator {
return $this->paginationService->getPaginatedDataGenerator(
$this,
$payload,
$type,
$offset,
$total,
$lastRecordId
);
}
/**
* Execute a search request against HubSpot CRM objects with rate limiting.
*
* @param string $objectType The object type ('deals', 'companies', 'contacts', 'calls')
* @param array<string, mixed> $payload The search payload with filters, sorts, properties, etc.
* @return array The search response with 'results', 'total', 'paging' keys
* @throws RateLimitException When rate limit is hit
* @throws HubspotException On API errors
*/
public function search(string $objectType, array $payload): array
{
$endpoint = self::BASE_URL . "/crm/v3/objects/{$objectType}/search";
return $this->executeRequest(function () use ($endpoint, $payload) {
$response = $this->getInstance()->getClient()->request('POST', $endpoint, ['json' => $payload]);
return $response->toArray();
});
}
/**
* @throws DealApiException
* @throws CrmException
*/
public function getOpportunityById(string $crmId, array $fields): array
{
try {
// $deal = $this->executeRequest(fn () => $this->getNewInstance()->crm()->deals()->basicApi()->getById(
$deal = $this->getNewInstance()->crm()->deals()->basicApi()->getById(
$crmId,
implode(',', $fields),
'companies,contacts'
);
} catch (DealApiException $e) {
$this->log->info('[Hubspot] Failed to fetch opportunity', [
'crm_id' => $crmId,
'reason' => $e->getMessage(),
]);
throw $e;
}
if (! $deal instanceof DealWithAssociations) {
throw new CrmException('Deal not found');
}
return [
'id' => $deal->getId(),
'properties' => $deal->getProperties(),
'associations' => $deal->getAssociations(),
];
}
/**
* Generic batch read method for HubSpot objects
*
* @param string $objectType The object type ('deals', 'companies', 'contacts')
* @param array<string> $crmIds Array of HubSpot object IDs (max 100)
* @param array<string> $fields Array of property names to fetch
*
* @return array<string, array> Array keyed by CRM ID with object data
*/
private function batchReadObjects(string $objectType, array $crmIds, array $fields): array
{
if (empty($crmIds)) {
return [];
}
$this->validateBatchSize($objectType, $crmIds);
$this->ensureValidToken();
try {
$batchConfig = $this->createBatchConfiguration($objectType);
$batchReadRequest = $this->prepareBatchRequest($batchConfig, $crmIds, $fields);
$response = $batchConfig['api']->read($batchReadRequest);
$this->validateApiResponse($response, $objectType);
$results = $this->processApiResults($response);
$this->logBatchResults($objectType, $crmIds, $results);
return $results;
} catch (\Throwable $e) {
$this->handleBatchError($e, $objectType, $crmIds);
}
}
private function validateBatchSize(string $objectType, array $crmIds): void
{
if (count($crmIds) > 100) {
throw new \InvalidArgumentException("Batch size cannot exceed 100 {$objectType}");
}
}
private function createBatchConfiguration(string $objectType): array
{
$configurations = [
'deals' => [
'batchReadRequest' => new \HubSpot\Client\Crm\Deals\Model\BatchReadInputSimplePublicObjectId(),
'inputClass' => \HubSpot\Client\Crm\Deals\Model\SimplePublicObjectId::class,
'api' => $this->getNewInstance()->crm()->deals()->batchApi(),
],
'companies' => [
'batchReadRequest' => new \HubSpot\Client\Crm\Companies\Model\BatchReadInputSimplePublicObjectId(),
'inputClass' => \HubSpot\Client\Crm\Companies\Model\SimplePublicObjectId::class,
'api' => $this->getNewInstance()->crm()->companies()->batchApi(),
],
'contacts' => [
'batchReadRequest' => new \HubSpot\Client\Crm\Contacts\Model\BatchReadInputSimplePublicObjectId(),
'inputClass' => \HubSpot\Client\Crm\Contacts\Model\SimplePublicObjectId::class,
'api' => $this->getNewInstance()->crm()->contacts()->batchApi(),
],
];
if (! isset($configurations[$objectType])) {
throw new \InvalidArgumentException("Unsupported object type: {$objectType}");
}
return $configurations[$objectType];
}
private function prepareBatchRequest(array $batchConfig, array $crmIds, array $fields): object
{
$batchReadRequest = $batchConfig['batchReadRequest'];
$inputClass = $batchConfig['inputClass'];
$inputs = array_map(function ($crmId) use ($inputClass) {
$input = new $inputClass();
$input->setId($crmId);
return $input;
}, $crmIds);
$batchReadRequest->setInputs($inputs);
$batchReadRequest->setProperties($fields);
return $batchReadRequest;
}
private function validateApiResponse($response, string $objectType): void
{
if (! $response) {
throw new CrmException("HubSpot API returned null response for {$objectType} batch read");
}
}
private function processApiResults($response): array
{
$results = [];
$responseResults = $response->getResults();
if ($responseResults) {
foreach ($responseResults as $object) {
if ($object && $object->getId()) {
$results[$object->getId()] = [
'id' => $object->getId(),
'properties' => $object->getProperties() ?: [],
];
}
}
}
return $results;
}
private function logBatchResults(string $objectType, array $crmIds, array $results): void
{
$this->log->info("[HubSpot] Batch fetched {$objectType}", [
'requested_count' => count($crmIds),
'returned_count' => count($results),
'crm_ids' => $crmIds,
]);
}
private function handleBatchError(\Throwable $e, string $objectType, array $crmIds): void
{
$errorMessage = $e->getMessage() ?: 'Unknown error';
$errorTrace = $e->getTraceAsString() ?: 'No trace available';
$this->log->error("[HubSpot] Failed to batch fetch {$objectType}", [
'crm_ids' => $crmIds,
'error' => $errorMessage,
'trace' => $errorTrace,
]);
throw new CrmException("Failed to batch fetch {$objectType}: " . $errorMessage);
}
/**
* Batch read multiple opportunities by their CRM IDs
*
* @param array<string> $crmIds Array of HubSpot deal IDs (max 100)
* @param array<string> $fields Array of property names to fetch
*
* @return array<string, array> Array keyed by CRM ID with opportunity data
*/
public function getOpportunitiesByIds(array $crmIds, array $fields): array
{
return $this->batchReadObjects('deals', $crmIds, $fields);
}
/**
* Batch read multiple companies by their CRM IDs
*
* @param array<string> $crmIds Array of HubSpot company IDs (max 100)
* @param array<string> $fields Array of property names to fetch
*
* @return array<string, array> Array keyed by CRM ID with company data
*/
public function getCompaniesByIds(array $crmIds, array $fields): array
{
return $this->batchReadObjects('companies', $crmIds, $fields);
}
/**
* Batch read multiple contacts by their CRM IDs
*
* @param array<string> $crmIds Array of HubSpot contact IDs (max 100)
* @param array<string> $fields Array of property names to fetch
*
* @return array<string, array> Array keyed by CRM ID with contact data
*/
public function getContactsByIds(array $crmIds, array $fields): array
{
return $this->batchReadObjects('contacts', $crmIds, $fields);
}
/**
* @throws CompanyApiException
* @throws CrmException
*/
public function getAccountById(string $crmId, array $fields): array
{
try {
$company = $this->getNewInstance()->crm()->companies()->basicApi()->getById(
$crmId,
implode(',', $fields),
);
} catch (CompanyApiException $e) {
$this->log->info('[Hubspot] Failed to fetch account', [
'crm_id' => $crmId,
'reason' => $e->getMessage(),
]);
throw $e;
}
if (! $company instanceof CompaniesWithAssociations) {
throw new CrmException('Account not found');
}
return [
'id' => $company->getId(),
'properties' => $company->getProperties(),
];
}
/**
* @throws ContactApiException
* @throws CrmException
*/
public function getContactById(string $crmId, array $fields): array
{
try {
$contact = $this->getNewInstance()->crm()->contacts()->basicApi()->getById(
$crmId,
implode(',', $fields)
);
} catch (ContactApiException $e) {
$this->log->info('[Hubspot] Failed to fetch contact', [
'crm_id' => $crmId,
'reason' => $e->getMessage(),
]);
throw $e;
}
if (! $contact instanceof ContactsWithAssociations) {
throw new CrmException('Contact not found');
}
return [
'id' => $contact->getId(),
'properties' => $contact->getProperties(),
];
}
/**
* This is email search request that Hubspot offers as GET (more generous quota)
*/
public function getContactByEmail(string $email, array $fields = []): array
{
try {
$contact = $this->getNewInstance()->crm()->contacts()->basicApi()->getById(
$email,
implode(',', $fields),
null,
false,
'email'
);
return [
'id' => $contact->getId(),
'properties' => $contact->getProperties(),
];
} catch (ContactApiException $e) {
$this->log->info('[Hubspot] Failed to fetch contact', [
'email' => $email,
'reason' => $e->getMessage(),
]);
return [];
}
}
/**
* @throws CrmException
*/
public function fetchProperty(string $objectType, string $propertyId): Property
{
$result = $this->getNewInstance()->crm()->properties()->coreApi()->getByName($objectType, $propertyId);
if (! $result instanceof Property) {
$this->log->error('[Hubspot] Failed to fetch property', [
'object_type' => $objectType,
'property_id' => $propertyId,
'reason' => $result->getMessage(),
]);
throw new CrmException('Failed to fetch property');
}
return $result;
}
/**
* @return array<CrmFieldOption>
*/
public function fetchPropertyOptions(string $objectType, string $propertyId): array
{
/** @var array<CrmFieldOption> */
return $this->fetchProperty($objectType, $propertyId)->getOptions();
}
/**
* @return array<array{id:string, label:string, deleted:bool}>
*/
public function fetchCallDispositions(): array
{
/** @var Response $response */
$response = $this->getInstance()->engagements()->getCallDispositions();
/**
* @var array<array{
* id:string,
* label:string,
* deleted: bool
* }>
*/
return $response->toArray();
}
/**
* @return array<CrmFieldOption>
*/
public function fetchOpportunityPipelineStages(): array
{
$stages = [];
$apiResponse = $this->getNewInstance()->crm()->pipelines()->pipelinesApi()->getAll('deals');
if ($apiResponse instanceof Error) {
$this->log->error('[Hubspot] Failed to fetch opportunity pipelines', [
'reason' => $apiResponse->getMessage(),
]);
return [];
}
foreach ($apiResponse->getResults() as $pipeline) {
$pipelineStages = array_map(
static function (PipelineStage $stage) {
return [
'id' => $stage->getId(),
'label' => $stage->getLabel(),
];
},
$pipeline->getStages()
);
$stages = array_merge($stages, $pipelineStages);
}
return $stages;
}
public function fetchOpportunityPipelines(): array
{
$pipelines = [];
try {
$apiResponse = $this->makeRequest('/crm/v3/pipelines/deals');
} catch (\Exception $e) {
$this->log->info('[Hubspot] Failed to fetch opportunity pipelines', [
'reason' => $e->getMessage(),
]);
return [];
}
$response = $apiResponse->toArray();
foreach ($response['results'] as $pipeline) {
$pipelines[] = [
'id' => $pipeline['id'],
'label' => $pipeline['label'],
];
}
return $pipelines;
}
/**
* @return array<CrmFieldOption>
*/
public function fetchMeetingOutcomeFieldOptions(Field $field): array
{
return $field->getCrmProviderId() === 'meetingOutcome'
? $this->fetchMeetingOutcomeTypes()
: $this->fetchCallActivityTypes();
}
public function fetchMeetingOutcomeTypes(): array
{
return $this->extractMeetingTypeOptions(
'[URL_WITH_CREDENTIALS] Response $response */
$response = $this->getInstance()
->getClient()
->request('GET', $endpoint);
/**
* @var array<array{
* value: string,
* label: string,
* displayOrder: int
* }> $optionData
*/
$optionData = $response->toArray()['options'] ?? [];
$options = [];
foreach ($optionData as $item) {
$options[] = [
'id' => $item['value'],
'value' => $item['value'],
'label' => $item['label'],
'display_order' => $item['displayOrder'],
];
}
return $options;
}
/**
* @return array<CrmFieldOption>
*/
public function fetchDispositionFieldOptions(): array
{
$options = [];
$dispositions = $this->fetchCallDispositions();
foreach ($dispositions as $disposition) {
if ($disposition['deleted'] !== false) {
continue;
}
$option['value'] = $disposition['id'];
$option['id'] = $disposition['id'];
$option['label'] = $disposition['label'];
$options[] = $option;
}
return $options;
}
/**
* @return array<CrmFieldOption>
*/
public function fetchOpportunityFieldOptions(Field $field): array
{
if ($field->isStageField()) {
return $this->fetchOpportunityPipelineStages();
}
if ($field->isPipelineField()) {
return $this->fetchOpportunityPipelines();
}
return $this->fetchPropertyOptions('deals', $field->getCrmProviderId());
}
/**
* @throws BadRequest
* @throws HubspotException
*/
public function makeRequest(string $endpoint, $method = 'GET', $payload = [], ?string $queryString = null)
{
$endpoint = self::BASE_URL . $endpoint;
if ($method === 'GET') {
return $this->getInstance()->getClient()?->request(
method: $method,
endpoint: $endpoint,
query_string: $queryString
);
} else {
return $this->getInstance()->getClient()->request($method, $endpoint, [
'json' => ($payload),
]);
}
}
/**
* @throws BadRequest
* @throws HubspotException
*/
public function createMeeting(array $payload): Response
{
$endpoint = '/crm/v3/objects/meetings';
return $this->makeRequest($endpoint, 'POST', $payload);
}
/**
* @throws BadRequest
* @throws HubspotException
*/
public function updateMeeting(string $meetingId, array $payload): Response
{
$endpoint = '/crm/v3/objects/meetings/' . $meetingId;
return $this->makeRequest($endpoint, 'PATCH', $payload);
}
/**
* @throws \Exception
*/
public function createNote(
string $body,
string $ownerId,
int $timestamp,
string $objectId,
NoteObject $noteObject
): ?string {
try {
$noteInput = new SimplePublicObjectInput([
'properties' => [
'hs_note_body' => $body,
'hubspot_owner_id' => $ownerId,
'hs_timestamp' => $timestamp,
],
]);
// Create note
$note = $this->getNewInstance()->crm()->objects()->basicApi()->create('note', $noteInput);
$this->getNewInstance()->crm()->objects()->associationsApi()->create(
'note',
$note->getId(),
$this->getNoteObject($noteObject),
$objectId,
$this->getNoteAssociationType($noteObject),
);
return $note->getId();
} catch (\Exception $e) {
$this->log->error('[Hubspot] Failed to create note', [
'objectId' => $objectId,
'noteObject' => $noteObject->getObjectType(),
'reason' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
return null;
}
public function updateEngagement(string $objectId, array $engagement, array $metadata): void
{
$this->getInstance()->engagements()->update($objectId, $engagement, $metadata);
}
public function getEngagementData(string $engagementId): array
{
$engagement = $this->getInstance()->engagements()->get($engagementId);
return $engagement->toArray();
}
public function createEngagement(array $engagement, array $associations, array $metadata): Response
{
return $this->getInstance()
->engagements()
->create($engagement, $associations, $metadata);
}
public function isUnauthorizedException(\Exception $e): bool
{
// Check for specific HubSpot API exception types first
if ($e instanceof BadRequest) {
// BadRequest can contain 401 status codes
return $e->getCode() === 401;
}
// Check for HTTP client exceptions with status codes
if ($e instanceof \GuzzleHttp\Exception\RequestException && $e->hasResponse()) {
$response = $e->getResponse();
if ($response !== null) {
return $response->getStatusCode() === 401;
}
}
// Check for Guzzle HTTP exceptions
if ($e instanceof \GuzzleHttp\Exception\ClientException) {
return $e->getCode() === 401;
}
// Fallback to string matching as last resort, but be more specific
$message = strtolower($e->getMessage());
return str_contains($message, '401 unauthorized') ||
str_contains($message, 'http 401') ||
str_contains($message, 'status code 401') ||
(preg_match('/\b401\b/', $message) && str_contains($message, 'unauthorized'));
}
/**
* Validates and refreshes the access token if needed before API requests.
* This ensures long-running processes don't fail due to token expiration.
*
* @throws SocialAccountTokenInvalidException
*/
public function ensureValidToken(): void
{
if ($this->oauthAccount === null) {
return;
}
$newToken = $this->tokenManager->ensureValidToken($this->oauthAccount);
if ($newToken !== null) {
$this->accessToken = $newToken;
}
}
public function getConfig()
{
return $this->config;
}
// returns only active (archived=false)
public function getOwners(): array
{
return $this->getNewInstance()->crm()->owners()->getAll();
}
/**
* @param bool $archived
*
* @return array<Owner>|[]
*/
public function getOwnersArchived(bool $archived = true): array
{
$endpoint = '/crm/v3/owners';
$queryParams = [
'archived' => $archived ? 'true' : 'false',
];
$queryString = http_build_query($queryParams);
$owners = [];
try {
$response = $this->makeRequest(endpoint: $endpoint, queryString: $queryString);
$responseData = $response?->toArray();
foreach ($responseData['results'] as $result) {
try {
$owners[] = Owner::create($result);
} catch (Throwable $e) {
$this->log->error('[HubSpot] Failed to process owner data', [
'result' => $result,
'error' => $e->getMessage(),
]);
continue;
}
}
} catch (Throwable $e) {
$this->log->error('HubSpot] Failed to fetch owners', [
'archived' => $archived,
'error' => $e->getMessage(),
]);
return [];
}
return $owners;
}
public function getMeeting(string $engagementId): ObjectWithAssociations
{
return $this->getNewInstance()->crm()->objects()->basicApi()
->getById('meeting', $engagementId, null, 'contact,company,deal');
}
public function deleteEngagement(string $engagementId): void
{
$this->getInstance()->engagements()->delete((int) $engagementId);
}
public function getAssociationsData(array $ids, string $fromObject, string $toObject): array
{
$associationData = [];
$idChunks = array_chunk($ids, self::ASSOCIATIONS_BATCH_SIZE_LIMIT);
foreach ($idChunks as $idChunk) {
try {
$batchInput = new \HubSpot\Client\Crm\Associations\Model\BatchInputPublicObjectId();
$batchInput->setInputs(array_map(function ($id) {
$publicObjectId = new \HubSpot\Client\Crm\Associations\Model\PublicObjectId();
$publicObjectId->setId($id);
return $publicObjectId;
}, $idChunk));
$associatedObjectsData = $this
->getNewInstance()
->crm()
->associations()
->batchApi()
->read($fromObject, $toObject, $batchInput);
if ($associatedObjectsData instanceof \HubSpot\Client\Crm\Associations\Model\BatchResponsePublicAssociationMulti) {
foreach ($associatedObjectsData->getResults() as $association) {
$from = $association->getFrom()->getId();
$toAssociations = $association->getTo();
if (! empty($toAssociations)) {
$associationData[$from] = array_map(function ($item) {
return $item->getId();
}, $toAssociations);
}
}
}
// } catch (RateLimitException $e) {
// throw $e;
} catch (\Exception $e) {
$this->log->error('[Hubspot] Failed to fetch associations', [
'from_object' => $fromObject,
'to_object' => $toObject,
'reason' => $e->getMessage(),
]);
}
}
return $associationData;
}
/**
* @throws \Exception
*/
private function getNoteAssociationType(NoteObject $noteObject): string
{
return match($noteObject) {
NoteObject::Opportunity => 'note_to_deal',
NoteObject::Lead, NoteObject::Contact => 'note_to_contact', // or 'note_to_lead' if your portal supports it
NoteObject::Account => 'note_to_company',
NoteObject::Call, NoteObject::Event => throw new \Exception('Not supported'),
};
}
/**
* @throws \Exception
*/
private function getNoteObject(NoteObject $noteObject): string
{
return match($noteObject) {
NoteObject::Opportunity => 'deal',
NoteObject::Lead, NoteObject::Contact => 'contact',
NoteObject::Account => 'company',
NoteObject::Call, NoteObject::Event => throw new \Exception('Not supported'),
};
}
public function addAssociations(string $objectType, string $associationType, array $payload): Response
{
$endpoint = "/crm/v4/associations/$objectType/$associationType/batch/create";
return $this->makeRequest($endpoint, 'POST', $payload);
}
public function removeAssociations(string $objectType, string $associationType, array $payload): Response
{
$endpoint = "/crm/v4/associations/$objectType/$associationType/batch/archive";
return $this->makeRequest($endpoint, 'POST', $payload);
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
19
Previous Highlighted Error
Next Highlighted Error
[2026-05-07 14:21:15] local.INFO: [Hubspot] DEBUG Getting headers {
"headers":{
"Date":["Thu,07 May 2026 14:21:15 GMT"],
"Content-Type":["application/json;charset=utf-8"],
"Transfer-Encoding":["chunked"],
"Connection":["keep-alive"],
"CF-Ray":["9f80deb8db60dc3a-SOF"],
"CF-Cache-Status":["DYNAMIC"],
"Strict-Transport-Security":["max-age=31536000; includeSubDomains; preload"],
"Vary":["origin,
accept-encoding"],
"access-control-allow-credentials":["false"],
"server-timing":["hcid;desc=\"019e02d0-6fd8-7812-bdba-885b7ccb3ee3\",
cfr;desc=\"9f80deb8e7c6dc3a-IAD\""],
"x-content-type-options":["nosniff"],
"x-hubspot-correlation-id":["019e02d0-6fd8-7812-bdba-885b7ccb3ee3"],
"Set-Cookie":["__cf_bm=SIUrtdQgXVrik50pdqF6hZVYKhzTnQBidvMabeCtm0Y-1778163675-[IP_ADDRESS]-rI.ZggtDKxTge5zr8_2gbBfWMQQ.ufZEXDZyHz2mBUFdzdo2gTHEsOkXMSEShjK0hGYxNhUGM1ZoBpX7BcFZcHEjA7Cs_.SMUhUnd2nYjko; path=/; expires=Thu,
07-May-26 14:51:15 GMT; domain=.hubapi.com; HttpOnly; Secure; SameSite=None"],
"Report-To":["{
\"endpoints\":[{
\"url\":\"https:\\/\\/a.nel.cloudflare.com\\/report\\/v4?s=NYAlsVTP0fYm32qrSDjxYE4sd2RWRqiSp3wHsmdEgZlzoYdxI%2BIxVpHmsKn3O%2BKVA3mFIJ2m7YRECDGSM%2BW2IYTzo6FM4%2BdUIjURO8srzKSvJgZ%2BQ6R79arKQw3uHLlX\"}],
\"group\":\"cf-nel\",
\"max_age\":604800}"],
"NEL":["{
\"success_fraction\":0.01,
\"report_to\":\"cf-nel\",
\"max_age\":604800}"],
"Server":["cloudflare"]}} {
"correlation_id":"95236535-ec98-4541-b92a-adfa73b69eab",
"trace_id":"c7ab8365-903f-46d4-9403-0e5b551e3545"}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
18188
|
790
|
0
|
2026-05-11T11:03:33.055619+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-11/1778 /Users/lukas/.screenpipe/data/data/2026-05-11/1778497413055_m1.jpg...
|
PhpStorm
|
faVsco.js – Client.php
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
JY-20725-handle-HS-search Project: faVsco.js, menu
JY-20725-handle-HS-search-rate-limit, menu
Start Listening for PHP Debug Connections
HandleHubspotRateLimitTest
Run 'HandleHubspotRateLimitTest'
Debug 'HandleHubspotRateLimitTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
2
67
3
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Services\Crm\Hubspot;
use HubSpot\Client\Crm\Deals\ApiException as DealApiException;
use HubSpot\Client\Crm\Contacts\ApiException as ContactApiException;
use HubSpot\Client\Crm\Companies\ApiException as CompanyApiException;
use HubSpot\Client\Crm\Contacts\Model\SimplePublicObjectWithAssociations as ContactsWithAssociations;
use HubSpot\Client\Crm\Companies\Model\SimplePublicObjectWithAssociations as CompaniesWithAssociations;
use HubSpot\Client\Crm\Deals\Model\SimplePublicObjectWithAssociations as DealWithAssociations;
use HubSpot\Client\Crm\Objects\Model\SimplePublicObjectInput;
use HubSpot\Client\Crm\Objects\Model\SimplePublicObjectWithAssociations as ObjectWithAssociations;
use HubSpot\Client\Crm\Pipelines\Model\Error;
use HubSpot\Client\Crm\Pipelines\Model\PipelineStage;
use HubSpot\Client\Crm\Properties\Model\Property;
use HubSpot\Discovery\Discovery;
use Jiminny\Exceptions\CrmException;
use Jiminny\Exceptions\RateLimitException;
use Jiminny\Exceptions\SocialAccountTokenInvalidException;
use Jiminny\Jobs\Crm\NoteObject;
use Jiminny\Models\Crm\Field;
use Jiminny\Services\Crm\BaseClient;
use Jiminny\Services\Crm\Hubspot\DTO\Response\Owner;
use Jiminny\Services\SocialAccountService;
use SevenShores\Hubspot\Exceptions\BadRequest;
use SevenShores\Hubspot\Exceptions\HubspotException;
use SevenShores\Hubspot\Factory;
use SevenShores\Hubspot\Http\Response;
use Jiminny\Services\Crm\Hubspot\Pagination\HubspotPaginationService;
use Illuminate\Support\Facades\Redis;
use Throwable;
/**
* @phpstan-type CrmFieldOption array{id:string, label:string, value?:string}
*/
class Client extends BaseClient implements HubspotClientInterface
{
public const string MIN_API_VERSION = '2';
public const string BASE_URL = '[URL_WITH_CREDENTIALS] T
* @param callable(): T $apiCall
* @return T
*
* @throws RateLimitException
*/
private function executeRequest(callable $apiCall)
{
$cacheKey = $this->getRateLimitCacheKey();
$cachedRetryAfter = Redis::get($cacheKey);
if (is_string($cachedRetryAfter) && is_numeric($cachedRetryAfter)) {
throw new RateLimitException(
'Hubspot rate limit (cached circuit-breaker)',
(int) $cachedRetryAfter,
);
}
try {
return $apiCall();
} catch (Throwable $e) {
if ($this->isHubspotRateLimit($e)) {
$retryAfter = $this->parseRetryAfter($e);
Redis::setex($cacheKey, $retryAfter, (string) $retryAfter);
$this->log->warning('[Hubspot] Received 429 from API', [
'team_id' => $this->config->team_id,
'config_id' => $this->config->getId(),
'retry_after' => $retryAfter,
'message' => $this->parseMessage($e),
'reason' => $e->getMessage(),
]);
throw new RateLimitException('Hubspot returned 429', $retryAfter, $e);
}
throw $e;
}
}
private function getRateLimitCacheKey(): string
{
return sprintf('hubspot:ratelimit:portal:%d', $this->config->getId());
}
public function isHubspotRateLimit(Throwable $e): bool
{
if ($e instanceof BadRequest
|| $e instanceof DealApiException
|| $e instanceof ContactApiException
|| $e instanceof CompanyApiException
|| $e instanceof \GuzzleHttp\Exception\RequestException
) {
return (int) $e->getCode() === 429;
}
return false;
}
public function parseRetryAfter(Throwable $e): int
{
if (method_exists($e, 'getResponseHeaders')) {
$headers = $e->getResponseHeaders() ?: [];
$value = $headers['Retry-After'] ?? $headers['retry-after'] ?? null;
if (is_array($value)) {
$value = $value[0] ?? null;
}
if (is_numeric($value)) {
return (int) $value;
}
}
$message = strtolower($this->parseMessage($e));
if (str_contains($message, 'daily')) {
return 600;
}
if (str_contains($message, 'ten secondly')) {
return 10;
}
if (str_contains($message, 'secondly')) {
return 1;
}
$this->log->warning('[Hubspot] No retry-after header or known message, using default', [
'exception_class' => get_class($e),
'message' => $message,
]);
return 10;
}
public function parseMessage(Throwable $e): string
{
if (! method_exists($e, 'getResponseBody')) {
return '';
}
$body = $e->getResponseBody();
if (is_string($body)) {
$body = json_decode($body, true) ?? [];
}
if (! is_array($body)) {
return '';
}
return (string) ($body['message'] ?? '');
}
public function getMinimumApiVersion(): string
{
return self::MIN_API_VERSION;
}
public function getInstance(): Factory
{
return new Factory([
'key' => $this->accessToken,
'oauth2' => true,
'base_url' => $this->baseUrl,
]);
}
public function getNewInstance(): Discovery
{
return \HubSpot\Factory::createWithAccessToken($this->accessToken);
}
/**
* Secondly and daily limits for Hubspot API
*
* Product Tier: Free & Starter | Professional & Enterprise | API add-on (any tier)
* Burst: 100/10 seconds | 150/10 seconds | 200/10 seconds
* Daily: 250,000 | 500,000 | 1,000,000
*
* Official documentation states: The search endpoints are rate limited to five requests per second.
* Since with 5 RPS were still hitting secondly rate limits we lowered it to 4
*/
public function getPaginatedData(array $payload, string $type, int $offset = 0): array
{
$total = 0;
$lastId = null;
$rows = [];
foreach ($this->getPaginatedDataGenerator($payload, $type, $offset, $total, $lastId) as $row) {
$rows[] = $row;
}
return ['results' => $rows, 'total' => $total, 'last_record' => $lastId];
}
/**
* @throws HubspotException
* @throws SocialAccountTokenInvalidException
* @throws BadRequest
*/
public function getPaginatedDataGenerator(
array $payload,
string $type,
int $offset = 0,
int &$total = 0,
?string &$lastRecordId = null
): \Generator {
return $this->paginationService->getPaginatedDataGenerator(
$this,
$payload,
$type,
$offset,
$total,
$lastRecordId
);
}
/**
* Execute a search request against HubSpot CRM objects with rate limiting.
*
* @param string $objectType The object type ('deals', 'companies', 'contacts', 'calls')
* @param array<string, mixed> $payload The search payload with filters, sorts, properties, etc.
* @return array The search response with 'results', 'total', 'paging' keys
* @throws RateLimitException When rate limit is hit
* @throws HubspotException On API errors
*/
public function search(string $objectType, array $payload): array
{
$endpoint = self::BASE_URL . "/crm/v3/objects/{$objectType}/search";
return $this->executeRequest(function () use ($endpoint, $payload) {
$response = $this->getInstance()->getClient()->request('POST', $endpoint, ['json' => $payload]);
return $response->toArray();
});
}
/**
* @throws DealApiException
* @throws CrmException
*/
public function getOpportunityById(string $crmId, array $fields): array
{
try {
// $deal = $this->executeRequest(fn () => $this->getNewInstance()->crm()->deals()->basicApi()->getById(
$deal = $this->getNewInstance()->crm()->deals()->basicApi()->getById(
$crmId,
implode(',', $fields),
'companies,contacts'
);
} catch (DealApiException $e) {
$this->log->info('[Hubspot] Failed to fetch opportunity', [
'crm_id' => $crmId,
'reason' => $e->getMessage(),
]);
throw $e;
}
if (! $deal instanceof DealWithAssociations) {
throw new CrmException('Deal not found');
}
return [
'id' => $deal->getId(),
'properties' => $deal->getProperties(),
'associations' => $deal->getAssociations(),
];
}
/**
* Generic batch read method for HubSpot objects
*
* @param string $objectType The object type ('deals', 'companies', 'contacts')
* @param array<string> $crmIds Array of HubSpot object IDs (max 100)
* @param array<string> $fields Array of property names to fetch
*
* @return array<string, array> Array keyed by CRM ID with object data
*/
private function batchReadObjects(string $objectType, array $crmIds, array $fields): array
{
if (empty($crmIds)) {
return [];
}
$this->validateBatchSize($objectType, $crmIds);
$this->ensureValidToken();
try {
$batchConfig = $this->createBatchConfiguration($objectType);
$batchReadRequest = $this->prepareBatchRequest($batchConfig, $crmIds, $fields);
$response = $batchConfig['api']->read($batchReadRequest);
$this->validateApiResponse($response, $objectType);
$results = $this->processApiResults($response);
$this->logBatchResults($objectType, $crmIds, $results);
return $results;
} catch (\Throwable $e) {
$this->handleBatchError($e, $objectType, $crmIds);
}
}
private function validateBatchSize(string $objectType, array $crmIds): void
{
if (count($crmIds) > 100) {
throw new \InvalidArgumentException("Batch size cannot exceed 100 {$objectType}");
}
}
private function createBatchConfiguration(string $objectType): array
{
$configurations = [
'deals' => [
'batchReadRequest' => new \HubSpot\Client\Crm\Deals\Model\BatchReadInputSimplePublicObjectId(),
'inputClass' => \HubSpot\Client\Crm\Deals\Model\SimplePublicObjectId::class,
'api' => $this->getNewInstance()->crm()->deals()->batchApi(),
],
'companies' => [
'batchReadRequest' => new \HubSpot\Client\Crm\Companies\Model\BatchReadInputSimplePublicObjectId(),
'inputClass' => \HubSpot\Client\Crm\Companies\Model\SimplePublicObjectId::class,
'api' => $this->getNewInstance()->crm()->companies()->batchApi(),
],
'contacts' => [
'batchReadRequest' => new \HubSpot\Client\Crm\Contacts\Model\BatchReadInputSimplePublicObjectId(),
'inputClass' => \HubSpot\Client\Crm\Contacts\Model\SimplePublicObjectId::class,
'api' => $this->getNewInstance()->crm()->contacts()->batchApi(),
],
];
if (! isset($configurations[$objectType])) {
throw new \InvalidArgumentException("Unsupported object type: {$objectType}");
}
return $configurations[$objectType];
}
private function prepareBatchRequest(array $batchConfig, array $crmIds, array $fields): object
{
$batchReadRequest = $batchConfig['batchReadRequest'];
$inputClass = $batchConfig['inputClass'];
$inputs = array_map(function ($crmId) use ($inputClass) {
$input = new $inputClass();
$input->setId($crmId);
return $input;
}, $crmIds);
$batchReadRequest->setInputs($inputs);
$batchReadRequest->setProperties($fields);
return $batchReadRequest;
}
private function validateApiResponse($response, string $objectType): void
{
if (! $response) {
throw new CrmException("HubSpot API returned null response for {$objectType} batch read");
}
}
private function processApiResults($response): array
{
$results = [];
$responseResults = $response->getResults();
if ($responseResults) {
foreach ($responseResults as $object) {
if ($object && $object->getId()) {
$results[$object->getId()] = [
'id' => $object->getId(),
'properties' => $object->getProperties() ?: [],
];
}
}
}
return $results;
}
private function logBatchResults(string $objectType, array $crmIds, array $results): void
{
$this->log->info("[HubSpot] Batch fetched {$objectType}", [
'requested_count' => count($crmIds),
'returned_count' => count($results),
'crm_ids' => $crmIds,
]);
}
private function handleBatchError(\Throwable $e, string $objectType, array $crmIds): void
{
$errorMessage = $e->getMessage() ?: 'Unknown error';
$errorTrace = $e->getTraceAsString() ?: 'No trace available';
$this->log->error("[HubSpot] Failed to batch fetch {$objectType}", [
'crm_ids' => $crmIds,
'error' => $errorMessage,
'trace' => $errorTrace,
]);
throw new CrmException("Failed to batch fetch {$objectType}: " . $errorMessage);
}
/**
* Batch read multiple opportunities by their CRM IDs
*
* @param array<string> $crmIds Array of HubSpot deal IDs (max 100)
* @param array<string> $fields Array of property names to fetch
*
* @return array<string, array> Array keyed by CRM ID with opportunity data
*/
public function getOpportunitiesByIds(array $crmIds, array $fields): array
{
return $this->batchReadObjects('deals', $crmIds, $fields);
}
/**
* Batch read multiple companies by their CRM IDs
*
* @param array<string> $crmIds Array of HubSpot company IDs (max 100)
* @param array<string> $fields Array of property names to fetch
*
* @return array<string, array> Array keyed by CRM ID with company data
*/
public function getCompaniesByIds(array $crmIds, array $fields): array
{
return $this->batchReadObjects('companies', $crmIds, $fields);
}
/**
* Batch read multiple contacts by their CRM IDs
*
* @param array<string> $crmIds Array of HubSpot contact IDs (max 100)
* @param array<string> $fields Array of property names to fetch
*
* @return array<string, array> Array keyed by CRM ID with contact data
*/
public function getContactsByIds(array $crmIds, array $fields): array
{
return $this->batchReadObjects('contacts', $crmIds, $fields);
}
/**
* @throws CompanyApiException
* @throws CrmException
*/
public function getAccountById(string $crmId, array $fields): array
{
try {
$company = $this->getNewInstance()->crm()->companies()->basicApi()->getById(
$crmId,
implode(',', $fields),
);
} catch (CompanyApiException $e) {
$this->log->info('[Hubspot] Failed to fetch account', [
'crm_id' => $crmId,
'reason' => $e->getMessage(),
]);
throw $e;
}
if (! $company instanceof CompaniesWithAssociations) {
throw new CrmException('Account not found');
}
return [
'id' => $company->getId(),
'properties' => $company->getProperties(),
];
}
/**
* @throws ContactApiException
* @throws CrmException
*/
public function getContactById(string $crmId, array $fields): array
{
try {
$contact = $this->getNewInstance()->crm()->contacts()->basicApi()->getById(
$crmId,
implode(',', $fields)
);
} catch (ContactApiException $e) {
$this->log->info('[Hubspot] Failed to fetch contact', [
'crm_id' => $crmId,
'reason' => $e->getMessage(),
]);
throw $e;
}
if (! $contact instanceof ContactsWithAssociations) {
throw new CrmException('Contact not found');
}
return [
'id' => $contact->getId(),
'properties' => $contact->getProperties(),
];
}
/**
* This is email search request that Hubspot offers as GET (more generous quota)
*/
public function getContactByEmail(string $email, array $fields = []): array
{
try {
$contact = $this->getNewInstance()->crm()->contacts()->basicApi()->getById(
$email,
implode(',', $fields),
null,
false,
'email'
);
return [
'id' => $contact->getId(),
'properties' => $contact->getProperties(),
];
} catch (ContactApiException $e) {
$this->log->info('[Hubspot] Failed to fetch contact', [
'email' => $email,
'reason' => $e->getMessage(),
]);
return [];
}
}
/**
* @throws CrmException
*/
public function fetchProperty(string $objectType, string $propertyId): Property
{
$result = $this->getNewInstance()->crm()->properties()->coreApi()->getByName($objectType, $propertyId);
if (! $result instanceof Property) {
$this->log->error('[Hubspot] Failed to fetch property', [
'object_type' => $objectType,
'property_id' => $propertyId,
'reason' => $result->getMessage(),
]);
throw new CrmException('Failed to fetch property');
}
return $result;
}
/**
* @return array<CrmFieldOption>
*/
public function fetchPropertyOptions(string $objectType, string $propertyId): array
{
/** @var array<CrmFieldOption> */
return $this->fetchProperty($objectType, $propertyId)->getOptions();
}
/**
* @return array<array{id:string, label:string, deleted:bool}>
*/
public function fetchCallDispositions(): array
{
/** @var Response $response */
$response = $this->getInstance()->engagements()->getCallDispositions();
/**
* @var array<array{
* id:string,
* label:string,
* deleted: bool
* }>
*/
return $response->toArray();
}
/**
* @return array<CrmFieldOption>
*/
public function fetchOpportunityPipelineStages(): array
{
$stages = [];
$apiResponse = $this->getNewInstance()->crm()->pipelines()->pipelinesApi()->getAll('deals');
if ($apiResponse instanceof Error) {
$this->log->error('[Hubspot] Failed to fetch opportunity pipelines', [
'reason' => $apiResponse->getMessage(),
]);
return [];
}
foreach ($apiResponse->getResults() as $pipeline) {
$pipelineStages = array_map(
static function (PipelineStage $stage) {
return [
'id' => $stage->getId(),
'label' => $stage->getLabel(),
];
},
$pipeline->getStages()
);
$stages = array_merge($stages, $pipelineStages);
}
return $stages;
}
public function fetchOpportunityPipelines(): array
{
$pipelines = [];
try {
$apiResponse = $this->makeRequest('/crm/v3/pipelines/deals');
} catch (\Exception $e) {
$this->log->info('[Hubspot] Failed to fetch opportunity pipelines', [
'reason' => $e->getMessage(),
]);
return [];
}
$response = $apiResponse->toArray();
foreach ($response['results'] as $pipeline) {
$pipelines[] = [
'id' => $pipeline['id'],
'label' => $pipeline['label'],
];
}
return $pipelines;
}
/**
* @return array<CrmFieldOption>
*/
public function fetchMeetingOutcomeFieldOptions(Field $field): array
{
return $field->getCrmProviderId() === 'meetingOutcome'
? $this->fetchMeetingOutcomeTypes()
: $this->fetchCallActivityTypes();
}
public function fetchMeetingOutcomeTypes(): array
{
return $this->extractMeetingTypeOptions(
'[URL_WITH_CREDENTIALS] Response $response */
$response = $this->getInstance()
->getClient()
->request('GET', $endpoint);
/**
* @var array<array{
* value: string,
* label: string,
* displayOrder: int
* }> $optionData
*/
$optionData = $response->toArray()['options'] ?? [];
$options = [];
foreach ($optionData as $item) {
$options[] = [
'id' => $item['value'],
'value' => $item['value'],
'label' => $item['label'],
'display_order' => $item['displayOrder'],
];
}
return $options;
}
/**
* @return array<CrmFieldOption>
*/
public function fetchDispositionFieldOptions(): array
{
$options = [];
$dispositions = $this->fetchCallDispositions();
foreach ($dispositions as $disposition) {
if ($disposition['deleted'] !== false) {
continue;
}
$option['value'] = $disposition['id'];
$option['id'] = $disposition['id'];
$option['label'] = $disposition['label'];
$options[] = $option;
}
return $options;
}
/**
* @return array<CrmFieldOption>
*/
public function fetchOpportunityFieldOptions(Field $field): array
{
if ($field->isStageField()) {
return $this->fetchOpportunityPipelineStages();
}
if ($field->isPipelineField()) {
return $this->fetchOpportunityPipelines();
}
return $this->fetchPropertyOptions('deals', $field->getCrmProviderId());
}
/**
* @throws BadRequest
* @throws HubspotException
*/
public function makeRequest(string $endpoint, $method = 'GET', $payload = [], ?string $queryString = null)
{
$endpoint = self::BASE_URL . $endpoint;
if ($method === 'GET') {
return $this->getInstance()->getClient()?->request(
method: $method,
endpoint: $endpoint,
query_string: $queryString
);
} else {
return $this->getInstance()->getClient()->request($method, $endpoint, [
'json' => ($payload),
]);
}
}
/**
* @throws BadRequest
* @throws HubspotException
*/
public function createMeeting(array $payload): Response
{
$endpoint = '/crm/v3/objects/meetings';
return $this->makeRequest($endpoint, 'POST', $payload);
}
/**
* @throws BadRequest
* @throws HubspotException
*/
public function updateMeeting(string $meetingId, array $payload): Response
{
$endpoint = '/crm/v3/objects/meetings/' . $meetingId;
return $this->makeRequest($endpoint, 'PATCH', $payload);
}
/**
* @throws \Exception
*/
public function createNote(
string $body,
string $ownerId,
int $timestamp,
string $objectId,
NoteObject $noteObject
): ?string {
try {
$noteInput = new SimplePublicObjectInput([
'properties' => [
'hs_note_body' => $body,
'hubspot_owner_id' => $ownerId,
'hs_timestamp' => $timestamp,
],
]);
// Create note
$note = $this->getNewInstance()->crm()->objects()->basicApi()->create('note', $noteInput);
$this->getNewInstance()->crm()->objects()->associationsApi()->create(
'note',
$note->getId(),
$this->getNoteObject($noteObject),
$objectId,
$this->getNoteAssociationType($noteObject),
);
return $note->getId();
} catch (\Exception $e) {
$this->log->error('[Hubspot] Failed to create note', [
'objectId' => $objectId,
'noteObject' => $noteObject->getObjectType(),
'reason' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
return null;
}
public function updateEngagement(string $objectId, array $engagement, array $metadata): void
{
$this->getInstance()->engagements()->update($objectId, $engagement, $metadata);
}
public function getEngagementData(string $engagementId): array
{
$engagement = $this->getInstance()->engagements()->get($engagementId);
return $engagement->toArray();
}
public function createEngagement(array $engagement, array $associations, array $metadata): Response
{
return $this->getInstance()
->engagements()
->create($engagement, $associations, $metadata);
}
public function isUnauthorizedException(\Exception $e): bool
{
// Check for specific HubSpot API exception types first
if ($e instanceof BadRequest) {
// BadRequest can contain 401 status codes
return $e->getCode() === 401;
}
// Check for HTTP client exceptions with status codes
if ($e instanceof \GuzzleHttp\Exception\RequestException && $e->hasResponse()) {
$response = $e->getResponse();
if ($response !== null) {
return $response->getStatusCode() === 401;
}
}
// Check for Guzzle HTTP exceptions
if ($e instanceof \GuzzleHttp\Exception\ClientException) {
return $e->getCode() === 401;
}
// Fallback to string matching as last resort, but be more specific
$message = strtolower($e->getMessage());
return str_contains($message, '401 unauthorized') ||
str_contains($message, 'http 401') ||
str_contains($message, 'status code 401') ||
(preg_match('/\b401\b/', $message) && str_contains($message, 'unauthorized'));
}
/**
* Validates and refreshes the access token if needed before API requests.
* This ensures long-running processes don't fail due to token expiration.
*
* @throws SocialAccountTokenInvalidException
*/
public function ensureValidToken(): void
{
if ($this->oauthAccount === null) {
return;
}
$newToken = $this->tokenManager->ensureValidToken($this->oauthAccount);
if ($newToken !== null) {
$this->accessToken = $newToken;
}
}
public function getConfig()
{
return $this->config;
}
// returns only active (archived=false)
public function getOwners(): array
{
return $this->getNewInstance()->crm()->owners()->getAll();
}
/**
* @param bool $archived
*
* @return array<Owner>|[]
*/
public function getOwnersArchived(bool $archived = true): array
{
$endpoint = '/crm/v3/owners';
$queryParams = [
'archived' => $archived ? 'true' : 'false',
];
$queryString = http_build_query($queryParams);
$owners = [];
try {
$response = $this->makeRequest(endpoint: $endpoint, queryString: $queryString);
$responseData = $response?->toArray();
foreach ($responseData['results'] as $result) {
try {
$owners[] = Owner::create($result);
} catch (Throwable $e) {
$this->log->error('[HubSpot] Failed to process owner data', [
'result' => $result,
'error' => $e->getMessage(),
]);
continue;
}
}
} catch (Throwable $e) {
$this->log->error('HubSpot] Failed to fetch owners', [
'archived' => $archived,
'error' => $e->getMessage(),
]);
return [];
}
return $owners;
}
public function getMeeting(string $engagementId): ObjectWithAssociations
{
return $this->getNewInstance()->crm()->objects()->basicApi()
->getById('meeting', $engagementId, null, 'contact,company,deal');
}
public function deleteEngagement(string $engagementId): void
{
$this->getInstance()->engagements()->delete((int) $engagementId);
}
public function getAssociationsData(array $ids, string $fromObject, string $toObject): array
{
$associationData = [];
$idChunks = array_chunk($ids, self::ASSOCIATIONS_BATCH_SIZE_LIMIT);
foreach ($idChunks as $idChunk) {
try {
$batchInput = new \HubSpot\Client\Crm\Associations\Model\BatchInputPublicObjectId();
$batchInput->setInputs(array_map(function ($id) {
$publicObjectId = new \HubSpot\Client\Crm\Associations\Model\PublicObjectId();
$publicObjectId->setId($id);
return $publicObjectId;
}, $idChunk));
$associatedObjectsData = $this
->getNewInstance()
->crm()
->associations()
->batchApi()
->read($fromObject, $toObject, $batchInput);
if ($associatedObjectsData instanceof \HubSpot\Client\Crm\Associations\Model\BatchResponsePublicAssociationMulti) {
foreach ($associatedObjectsData->getResults() as $association) {
$from = $association->getFrom()->getId();
$toAssociations = $association->getTo();
if (! empty($toAssociations)) {
$associationData[$from] = array_map(function ($item) {
return $item->getId();
}, $toAssociations);
}
}
}
// } catch (RateLimitException $e) {
// throw $e;
} catch (\Exception $e) {
$this->log->error('[Hubspot] Failed to fetch associations', [
'from_object' => $fromObject,
'to_object' => $toObject,
'reason' => $e->getMessage(),
]);
}
}
return $associationData;
}
/**
* @throws \Exception
*/
private function getNoteAssociationType(NoteObject $noteObject): string
{
return match($noteObject) {
NoteObject::Opportunity => 'note_to_deal',
NoteObject::Lead, NoteObject::Contact => 'note_to_contact', // or 'note_to_lead' if your portal supports it
NoteObject::Account => 'note_to_company',
NoteObject::Call, NoteObject::Event => throw new \Exception('Not supported'),
};
}
/**
* @throws \Exception
*/
private function getNoteObject(NoteObject $noteObject): string
{
return match($noteObject) {
NoteObject::Opportunity => 'deal',
NoteObject::Lead, NoteObject::Contact => 'contact',
NoteObject::Account => 'company',
NoteObject::Call, NoteObject::Event => throw new \Exception('Not supported'),
};
}
public function addAssociations(string $objectType, string $associationType, array $payload): Response
{
$endpoint = "/crm/v4/associations/$objectType/$associationType/batch/create";
return $this->makeRequest($endpoint, 'POST', $payload);
}
public function removeAssociations(string $objectType, string $associationType, array $payload): Response
{
$endpoint = "/crm/v4/associations/$objectType/$associationType/batch/archive";
return $this->makeRequest($endpoint, 'POST', $payload);
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
19
Previous Highlighted Error
Next Highlighted Error
[2026-05-07 14:21:15] local.INFO: [Hubspot] DEBUG Getting headers {
"headers":{
"Date":["Thu,07 May 2026 14:21:15 GMT"],
"Content-Type":["application/json;charset=utf-8"],
"Transfer-Encoding":["chunked"],
"Connection":["keep-alive"],
"CF-Ray":["9f80deb8db60dc3a-SOF"],
"CF-Cache-Status":["DYNAMIC"],
"Strict-Transport-Security":["max-age=31536000; includeSubDomains; preload"],
"Vary":["origin,
accept-encoding"],
"access-control-allow-credentials":["false"],
"server-timing":["hcid;desc=\"019e02d0-6fd8-7812-bdba-885b7ccb3ee3\",
cfr;desc=\"9f80deb8e7c6dc3a-IAD\""],
"x-content-type-options":["nosniff"],
"x-hubspot-correlation-id":["019e02d0-6fd8-7812-bdba-885b7ccb3ee3"],
"Set-Cookie":["__cf_bm=SIUrtdQgXVrik50pdqF6hZVYKhzTnQBidvMabeCtm0Y-1778163675-[IP_ADDRESS]-rI.ZggtDKxTge5zr8_2gbBfWMQQ.ufZEXDZyHz2mBUFdzdo2gTHEsOkXMSEShjK0hGYxNhUGM1ZoBpX7BcFZcHEjA7Cs_.SMUhUnd2nYjko; path=/; expires=Thu,
07-May-26 14:51:15 GMT; domain=.hubapi.com; HttpOnly; Secure; SameSite=None"],
"Report-To":["{
\"endpoints\":[{
\"url\":\"https:\\/\\/a.nel.cloudflare.com\\/report\\/v4?s=NYAlsVTP0fYm32qrSDjxYE4sd2RWRqiSp3wHsmdEgZlzoYdxI%2BIxVpHmsKn3O%2BKVA3mFIJ2m7YRECDGSM%2BW2IYTzo6FM4%2BdUIjURO8srzKSvJgZ%2BQ6R79arKQw3uHLlX\"}],
\"group\":\"cf-nel\",
\"max_age\":604800}"],
"NEL":["{
\"success_fraction\":0.01,
\"report_to\":\"cf-nel\",
\"max_age\":604800}"],
"Server":["cloudflare"]}} {
"correlation_id":"95236535-ec98-4541-b92a-adfa73b69eab",
"trace_id":"c7ab8365-903f-46d4-9403-0e5b551e3545"}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JY-20725-handle-HS-search-rate-limit, menu","depth":5,"on_screen":true,"help_text":"Git Branch: JY-20725-handle-HS-search-rate-limit","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"HandleHubspotRateLimitTest","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'HandleHubspotRateLimitTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'HandleHubspotRateLimitTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"2","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"67","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"3","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Services\\Crm\\Hubspot;\n\nuse HubSpot\\Client\\Crm\\Deals\\ApiException as DealApiException;\nuse HubSpot\\Client\\Crm\\Contacts\\ApiException as ContactApiException;\nuse HubSpot\\Client\\Crm\\Companies\\ApiException as CompanyApiException;\nuse HubSpot\\Client\\Crm\\Contacts\\Model\\SimplePublicObjectWithAssociations as ContactsWithAssociations;\nuse HubSpot\\Client\\Crm\\Companies\\Model\\SimplePublicObjectWithAssociations as CompaniesWithAssociations;\nuse HubSpot\\Client\\Crm\\Deals\\Model\\SimplePublicObjectWithAssociations as DealWithAssociations;\nuse HubSpot\\Client\\Crm\\Objects\\Model\\SimplePublicObjectInput;\nuse HubSpot\\Client\\Crm\\Objects\\Model\\SimplePublicObjectWithAssociations as ObjectWithAssociations;\nuse HubSpot\\Client\\Crm\\Pipelines\\Model\\Error;\nuse HubSpot\\Client\\Crm\\Pipelines\\Model\\PipelineStage;\nuse HubSpot\\Client\\Crm\\Properties\\Model\\Property;\nuse HubSpot\\Discovery\\Discovery;\nuse Jiminny\\Exceptions\\CrmException;\nuse Jiminny\\Exceptions\\RateLimitException;\nuse Jiminny\\Exceptions\\SocialAccountTokenInvalidException;\nuse Jiminny\\Jobs\\Crm\\NoteObject;\nuse Jiminny\\Models\\Crm\\Field;\nuse Jiminny\\Services\\Crm\\BaseClient;\nuse Jiminny\\Services\\Crm\\Hubspot\\DTO\\Response\\Owner;\nuse Jiminny\\Services\\SocialAccountService;\nuse SevenShores\\Hubspot\\Exceptions\\BadRequest;\nuse SevenShores\\Hubspot\\Exceptions\\HubspotException;\nuse SevenShores\\Hubspot\\Factory;\nuse SevenShores\\Hubspot\\Http\\Response;\nuse Jiminny\\Services\\Crm\\Hubspot\\Pagination\\HubspotPaginationService;\nuse Illuminate\\Support\\Facades\\Redis;\nuse Throwable;\n\n/**\n * @phpstan-type CrmFieldOption array{id:string, label:string, value?:string}\n */\nclass Client extends BaseClient implements HubspotClientInterface\n{\n public const string MIN_API_VERSION = '2';\n\n public const string BASE_URL = 'https://api.hubapi.com';\n\n public const int ASSOCIATIONS_BATCH_SIZE_LIMIT = 1000;\n\n private HubspotPaginationService $paginationService;\n private HubspotTokenManager $tokenManager;\n\n public function __construct(\n SocialAccountService $socialAccountService,\n HubspotPaginationService $paginationService,\n HubspotTokenManager $tokenManager\n ) {\n parent::__construct($socialAccountService);\n $this->paginationService = $paginationService;\n $this->tokenManager = $tokenManager;\n\n $this->setBaseUrl(self::BASE_URL);\n $this->setVersion(self::MIN_API_VERSION);\n }\n\n /**\n * Reacts to a rate limits (429) from HubSpot by translating it\n * into a RateLimitException carrying retry_after.\n *\n * Wrap any outbound HubSpot call (SDK or raw HTTP) like:\n *\n * $this->executeRequest(fn () => $this->getNewInstance()->crm()->...);\n *\n * @template T\n * @param callable(): T $apiCall\n * @return T\n *\n * @throws RateLimitException\n */\n private function executeRequest(callable $apiCall)\n {\n $cacheKey = $this->getRateLimitCacheKey();\n\n $cachedRetryAfter = Redis::get($cacheKey);\n if (is_string($cachedRetryAfter) && is_numeric($cachedRetryAfter)) {\n throw new RateLimitException(\n 'Hubspot rate limit (cached circuit-breaker)',\n (int) $cachedRetryAfter,\n );\n }\n\n try {\n return $apiCall();\n } catch (Throwable $e) {\n if ($this->isHubspotRateLimit($e)) {\n $retryAfter = $this->parseRetryAfter($e);\n\n Redis::setex($cacheKey, $retryAfter, (string) $retryAfter);\n\n $this->log->warning('[Hubspot] Received 429 from API', [\n 'team_id' => $this->config->team_id,\n 'config_id' => $this->config->getId(),\n 'retry_after' => $retryAfter,\n 'message' => $this->parseMessage($e),\n 'reason' => $e->getMessage(),\n ]);\n\n throw new RateLimitException('Hubspot returned 429', $retryAfter, $e);\n }\n\n throw $e;\n }\n }\n\n private function getRateLimitCacheKey(): string\n {\n return sprintf('hubspot:ratelimit:portal:%d', $this->config->getId());\n }\n\n public function isHubspotRateLimit(Throwable $e): bool\n {\n if ($e instanceof BadRequest\n || $e instanceof DealApiException\n || $e instanceof ContactApiException\n || $e instanceof CompanyApiException\n || $e instanceof \\GuzzleHttp\\Exception\\RequestException\n ) {\n return (int) $e->getCode() === 429;\n }\n\n return false;\n }\n\n public function parseRetryAfter(Throwable $e): int\n {\n if (method_exists($e, 'getResponseHeaders')) {\n $headers = $e->getResponseHeaders() ?: [];\n $value = $headers['Retry-After'] ?? $headers['retry-after'] ?? null;\n if (is_array($value)) {\n $value = $value[0] ?? null;\n }\n if (is_numeric($value)) {\n return (int) $value;\n }\n }\n\n $message = strtolower($this->parseMessage($e));\n\n if (str_contains($message, 'daily')) {\n return 600;\n }\n if (str_contains($message, 'ten secondly')) {\n return 10;\n }\n if (str_contains($message, 'secondly')) {\n return 1;\n }\n\n $this->log->warning('[Hubspot] No retry-after header or known message, using default', [\n 'exception_class' => get_class($e),\n 'message' => $message,\n ]);\n\n return 10;\n }\n\n public function parseMessage(Throwable $e): string\n {\n if (! method_exists($e, 'getResponseBody')) {\n return '';\n }\n\n $body = $e->getResponseBody();\n if (is_string($body)) {\n $body = json_decode($body, true) ?? [];\n }\n\n if (! is_array($body)) {\n return '';\n }\n\n return (string) ($body['message'] ?? '');\n }\n\n public function getMinimumApiVersion(): string\n {\n return self::MIN_API_VERSION;\n }\n\n public function getInstance(): Factory\n {\n return new Factory([\n 'key' => $this->accessToken,\n 'oauth2' => true,\n 'base_url' => $this->baseUrl,\n ]);\n }\n\n public function getNewInstance(): Discovery\n {\n return \\HubSpot\\Factory::createWithAccessToken($this->accessToken);\n }\n\n /**\n * Secondly and daily limits for Hubspot API\n *\n * Product Tier: Free & Starter | Professional & Enterprise | API add-on (any tier)\n * Burst: 100/10 seconds | 150/10 seconds | 200/10 seconds\n * Daily: 250,000 | 500,000 | 1,000,000\n *\n * Official documentation states: The search endpoints are rate limited to five requests per second.\n * Since with 5 RPS were still hitting secondly rate limits we lowered it to 4\n */\n public function getPaginatedData(array $payload, string $type, int $offset = 0): array\n {\n $total = 0;\n $lastId = null;\n $rows = [];\n foreach ($this->getPaginatedDataGenerator($payload, $type, $offset, $total, $lastId) as $row) {\n $rows[] = $row;\n }\n\n return ['results' => $rows, 'total' => $total, 'last_record' => $lastId];\n }\n\n /**\n * @throws HubspotException\n * @throws SocialAccountTokenInvalidException\n * @throws BadRequest\n */\n public function getPaginatedDataGenerator(\n array $payload,\n string $type,\n int $offset = 0,\n int &$total = 0,\n ?string &$lastRecordId = null\n ): \\Generator {\n return $this->paginationService->getPaginatedDataGenerator(\n $this,\n $payload,\n $type,\n $offset,\n $total,\n $lastRecordId\n );\n }\n\n /**\n * Execute a search request against HubSpot CRM objects with rate limiting.\n *\n * @param string $objectType The object type ('deals', 'companies', 'contacts', 'calls')\n * @param array<string, mixed> $payload The search payload with filters, sorts, properties, etc.\n * @return array The search response with 'results', 'total', 'paging' keys\n * @throws RateLimitException When rate limit is hit\n * @throws HubspotException On API errors\n */\n public function search(string $objectType, array $payload): array\n {\n $endpoint = self::BASE_URL . \"/crm/v3/objects/{$objectType}/search\";\n\n return $this->executeRequest(function () use ($endpoint, $payload) {\n $response = $this->getInstance()->getClient()->request('POST', $endpoint, ['json' => $payload]);\n\n return $response->toArray();\n });\n }\n\n /**\n * @throws DealApiException\n * @throws CrmException\n */\n public function getOpportunityById(string $crmId, array $fields): array\n {\n try {\n// $deal = $this->executeRequest(fn () => $this->getNewInstance()->crm()->deals()->basicApi()->getById(\n $deal = $this->getNewInstance()->crm()->deals()->basicApi()->getById(\n $crmId,\n implode(',', $fields),\n 'companies,contacts'\n );\n } catch (DealApiException $e) {\n $this->log->info('[Hubspot] Failed to fetch opportunity', [\n 'crm_id' => $crmId,\n 'reason' => $e->getMessage(),\n ]);\n\n throw $e;\n }\n\n if (! $deal instanceof DealWithAssociations) {\n throw new CrmException('Deal not found');\n }\n\n return [\n 'id' => $deal->getId(),\n 'properties' => $deal->getProperties(),\n 'associations' => $deal->getAssociations(),\n ];\n }\n\n /**\n * Generic batch read method for HubSpot objects\n *\n * @param string $objectType The object type ('deals', 'companies', 'contacts')\n * @param array<string> $crmIds Array of HubSpot object IDs (max 100)\n * @param array<string> $fields Array of property names to fetch\n *\n * @return array<string, array> Array keyed by CRM ID with object data\n */\n private function batchReadObjects(string $objectType, array $crmIds, array $fields): array\n {\n if (empty($crmIds)) {\n return [];\n }\n\n $this->validateBatchSize($objectType, $crmIds);\n $this->ensureValidToken();\n\n try {\n $batchConfig = $this->createBatchConfiguration($objectType);\n $batchReadRequest = $this->prepareBatchRequest($batchConfig, $crmIds, $fields);\n $response = $batchConfig['api']->read($batchReadRequest);\n\n $this->validateApiResponse($response, $objectType);\n\n $results = $this->processApiResults($response);\n $this->logBatchResults($objectType, $crmIds, $results);\n\n return $results;\n } catch (\\Throwable $e) {\n $this->handleBatchError($e, $objectType, $crmIds);\n }\n }\n\n private function validateBatchSize(string $objectType, array $crmIds): void\n {\n if (count($crmIds) > 100) {\n throw new \\InvalidArgumentException(\"Batch size cannot exceed 100 {$objectType}\");\n }\n }\n\n private function createBatchConfiguration(string $objectType): array\n {\n $configurations = [\n 'deals' => [\n 'batchReadRequest' => new \\HubSpot\\Client\\Crm\\Deals\\Model\\BatchReadInputSimplePublicObjectId(),\n 'inputClass' => \\HubSpot\\Client\\Crm\\Deals\\Model\\SimplePublicObjectId::class,\n 'api' => $this->getNewInstance()->crm()->deals()->batchApi(),\n ],\n 'companies' => [\n 'batchReadRequest' => new \\HubSpot\\Client\\Crm\\Companies\\Model\\BatchReadInputSimplePublicObjectId(),\n 'inputClass' => \\HubSpot\\Client\\Crm\\Companies\\Model\\SimplePublicObjectId::class,\n 'api' => $this->getNewInstance()->crm()->companies()->batchApi(),\n ],\n 'contacts' => [\n 'batchReadRequest' => new \\HubSpot\\Client\\Crm\\Contacts\\Model\\BatchReadInputSimplePublicObjectId(),\n 'inputClass' => \\HubSpot\\Client\\Crm\\Contacts\\Model\\SimplePublicObjectId::class,\n 'api' => $this->getNewInstance()->crm()->contacts()->batchApi(),\n ],\n ];\n\n if (! isset($configurations[$objectType])) {\n throw new \\InvalidArgumentException(\"Unsupported object type: {$objectType}\");\n }\n\n return $configurations[$objectType];\n }\n\n private function prepareBatchRequest(array $batchConfig, array $crmIds, array $fields): object\n {\n $batchReadRequest = $batchConfig['batchReadRequest'];\n $inputClass = $batchConfig['inputClass'];\n\n $inputs = array_map(function ($crmId) use ($inputClass) {\n $input = new $inputClass();\n $input->setId($crmId);\n\n return $input;\n }, $crmIds);\n\n $batchReadRequest->setInputs($inputs);\n $batchReadRequest->setProperties($fields);\n\n return $batchReadRequest;\n }\n\n private function validateApiResponse($response, string $objectType): void\n {\n if (! $response) {\n throw new CrmException(\"HubSpot API returned null response for {$objectType} batch read\");\n }\n }\n\n private function processApiResults($response): array\n {\n $results = [];\n $responseResults = $response->getResults();\n\n if ($responseResults) {\n foreach ($responseResults as $object) {\n if ($object && $object->getId()) {\n $results[$object->getId()] = [\n 'id' => $object->getId(),\n 'properties' => $object->getProperties() ?: [],\n ];\n }\n }\n }\n\n return $results;\n }\n\n private function logBatchResults(string $objectType, array $crmIds, array $results): void\n {\n $this->log->info(\"[HubSpot] Batch fetched {$objectType}\", [\n 'requested_count' => count($crmIds),\n 'returned_count' => count($results),\n 'crm_ids' => $crmIds,\n ]);\n }\n\n private function handleBatchError(\\Throwable $e, string $objectType, array $crmIds): void\n {\n $errorMessage = $e->getMessage() ?: 'Unknown error';\n $errorTrace = $e->getTraceAsString() ?: 'No trace available';\n\n $this->log->error(\"[HubSpot] Failed to batch fetch {$objectType}\", [\n 'crm_ids' => $crmIds,\n 'error' => $errorMessage,\n 'trace' => $errorTrace,\n ]);\n\n throw new CrmException(\"Failed to batch fetch {$objectType}: \" . $errorMessage);\n }\n\n /**\n * Batch read multiple opportunities by their CRM IDs\n *\n * @param array<string> $crmIds Array of HubSpot deal IDs (max 100)\n * @param array<string> $fields Array of property names to fetch\n *\n * @return array<string, array> Array keyed by CRM ID with opportunity data\n */\n public function getOpportunitiesByIds(array $crmIds, array $fields): array\n {\n return $this->batchReadObjects('deals', $crmIds, $fields);\n }\n\n /**\n * Batch read multiple companies by their CRM IDs\n *\n * @param array<string> $crmIds Array of HubSpot company IDs (max 100)\n * @param array<string> $fields Array of property names to fetch\n *\n * @return array<string, array> Array keyed by CRM ID with company data\n */\n public function getCompaniesByIds(array $crmIds, array $fields): array\n {\n return $this->batchReadObjects('companies', $crmIds, $fields);\n }\n\n /**\n * Batch read multiple contacts by their CRM IDs\n *\n * @param array<string> $crmIds Array of HubSpot contact IDs (max 100)\n * @param array<string> $fields Array of property names to fetch\n *\n * @return array<string, array> Array keyed by CRM ID with contact data\n */\n public function getContactsByIds(array $crmIds, array $fields): array\n {\n return $this->batchReadObjects('contacts', $crmIds, $fields);\n }\n\n /**\n * @throws CompanyApiException\n * @throws CrmException\n */\n public function getAccountById(string $crmId, array $fields): array\n {\n try {\n $company = $this->getNewInstance()->crm()->companies()->basicApi()->getById(\n $crmId,\n implode(',', $fields),\n );\n } catch (CompanyApiException $e) {\n $this->log->info('[Hubspot] Failed to fetch account', [\n 'crm_id' => $crmId,\n 'reason' => $e->getMessage(),\n ]);\n\n throw $e;\n }\n\n if (! $company instanceof CompaniesWithAssociations) {\n throw new CrmException('Account not found');\n }\n\n return [\n 'id' => $company->getId(),\n 'properties' => $company->getProperties(),\n ];\n }\n\n /**\n * @throws ContactApiException\n * @throws CrmException\n */\n public function getContactById(string $crmId, array $fields): array\n {\n try {\n $contact = $this->getNewInstance()->crm()->contacts()->basicApi()->getById(\n $crmId,\n implode(',', $fields)\n );\n } catch (ContactApiException $e) {\n $this->log->info('[Hubspot] Failed to fetch contact', [\n 'crm_id' => $crmId,\n 'reason' => $e->getMessage(),\n ]);\n\n throw $e;\n }\n\n if (! $contact instanceof ContactsWithAssociations) {\n throw new CrmException('Contact not found');\n }\n\n return [\n 'id' => $contact->getId(),\n 'properties' => $contact->getProperties(),\n ];\n }\n\n /**\n * This is email search request that Hubspot offers as GET (more generous quota)\n */\n public function getContactByEmail(string $email, array $fields = []): array\n {\n try {\n $contact = $this->getNewInstance()->crm()->contacts()->basicApi()->getById(\n $email,\n implode(',', $fields),\n null,\n false,\n 'email'\n );\n\n return [\n 'id' => $contact->getId(),\n 'properties' => $contact->getProperties(),\n ];\n } catch (ContactApiException $e) {\n $this->log->info('[Hubspot] Failed to fetch contact', [\n 'email' => $email,\n 'reason' => $e->getMessage(),\n ]);\n\n return [];\n }\n }\n\n /**\n * @throws CrmException\n */\n public function fetchProperty(string $objectType, string $propertyId): Property\n {\n $result = $this->getNewInstance()->crm()->properties()->coreApi()->getByName($objectType, $propertyId);\n\n if (! $result instanceof Property) {\n $this->log->error('[Hubspot] Failed to fetch property', [\n 'object_type' => $objectType,\n 'property_id' => $propertyId,\n 'reason' => $result->getMessage(),\n ]);\n\n throw new CrmException('Failed to fetch property');\n }\n\n return $result;\n }\n\n /**\n * @return array<CrmFieldOption>\n */\n public function fetchPropertyOptions(string $objectType, string $propertyId): array\n {\n /** @var array<CrmFieldOption> */\n return $this->fetchProperty($objectType, $propertyId)->getOptions();\n }\n\n /**\n * @return array<array{id:string, label:string, deleted:bool}>\n */\n public function fetchCallDispositions(): array\n {\n /** @var Response $response */\n $response = $this->getInstance()->engagements()->getCallDispositions();\n\n /**\n * @var array<array{\n * id:string,\n * label:string,\n * deleted: bool\n * }>\n */\n return $response->toArray();\n }\n\n /**\n * @return array<CrmFieldOption>\n */\n public function fetchOpportunityPipelineStages(): array\n {\n $stages = [];\n $apiResponse = $this->getNewInstance()->crm()->pipelines()->pipelinesApi()->getAll('deals');\n\n if ($apiResponse instanceof Error) {\n $this->log->error('[Hubspot] Failed to fetch opportunity pipelines', [\n 'reason' => $apiResponse->getMessage(),\n ]);\n\n return [];\n }\n\n foreach ($apiResponse->getResults() as $pipeline) {\n $pipelineStages = array_map(\n static function (PipelineStage $stage) {\n return [\n 'id' => $stage->getId(),\n 'label' => $stage->getLabel(),\n ];\n },\n $pipeline->getStages()\n );\n\n $stages = array_merge($stages, $pipelineStages);\n }\n\n return $stages;\n }\n\n public function fetchOpportunityPipelines(): array\n {\n $pipelines = [];\n\n try {\n $apiResponse = $this->makeRequest('/crm/v3/pipelines/deals');\n } catch (\\Exception $e) {\n $this->log->info('[Hubspot] Failed to fetch opportunity pipelines', [\n 'reason' => $e->getMessage(),\n ]);\n\n return [];\n }\n\n $response = $apiResponse->toArray();\n\n foreach ($response['results'] as $pipeline) {\n $pipelines[] = [\n 'id' => $pipeline['id'],\n 'label' => $pipeline['label'],\n ];\n }\n\n return $pipelines;\n }\n\n /**\n * @return array<CrmFieldOption>\n */\n public function fetchMeetingOutcomeFieldOptions(Field $field): array\n {\n return $field->getCrmProviderId() === 'meetingOutcome'\n ? $this->fetchMeetingOutcomeTypes()\n : $this->fetchCallActivityTypes();\n }\n\n public function fetchMeetingOutcomeTypes(): array\n {\n return $this->extractMeetingTypeOptions(\n 'https://api.hubapi.com/crm/v3/properties/meeting/hs_meeting_outcome'\n );\n }\n\n public function fetchCallActivityTypes(): array\n {\n return $this->extractMeetingTypeOptions(\n 'https://api.hubapi.com/crm/v3/properties/call/hs_activity_type'\n );\n }\n\n private function extractMeetingTypeOptions(string $endpoint): array\n {\n /** @var Response $response */\n $response = $this->getInstance()\n ->getClient()\n ->request('GET', $endpoint);\n\n /**\n * @var array<array{\n * value: string,\n * label: string,\n * displayOrder: int\n * }> $optionData\n */\n $optionData = $response->toArray()['options'] ?? [];\n\n $options = [];\n foreach ($optionData as $item) {\n $options[] = [\n 'id' => $item['value'],\n 'value' => $item['value'],\n 'label' => $item['label'],\n 'display_order' => $item['displayOrder'],\n ];\n }\n\n return $options;\n }\n\n /**\n * @return array<CrmFieldOption>\n */\n public function fetchDispositionFieldOptions(): array\n {\n $options = [];\n\n $dispositions = $this->fetchCallDispositions();\n\n foreach ($dispositions as $disposition) {\n if ($disposition['deleted'] !== false) {\n continue;\n }\n\n $option['value'] = $disposition['id'];\n $option['id'] = $disposition['id'];\n $option['label'] = $disposition['label'];\n\n $options[] = $option;\n }\n\n return $options;\n }\n\n /**\n * @return array<CrmFieldOption>\n */\n public function fetchOpportunityFieldOptions(Field $field): array\n {\n if ($field->isStageField()) {\n return $this->fetchOpportunityPipelineStages();\n }\n\n if ($field->isPipelineField()) {\n return $this->fetchOpportunityPipelines();\n }\n\n return $this->fetchPropertyOptions('deals', $field->getCrmProviderId());\n }\n\n /**\n * @throws BadRequest\n * @throws HubspotException\n */\n public function makeRequest(string $endpoint, $method = 'GET', $payload = [], ?string $queryString = null)\n {\n $endpoint = self::BASE_URL . $endpoint;\n\n if ($method === 'GET') {\n return $this->getInstance()->getClient()?->request(\n method: $method,\n endpoint: $endpoint,\n query_string: $queryString\n );\n } else {\n return $this->getInstance()->getClient()->request($method, $endpoint, [\n 'json' => ($payload),\n ]);\n }\n }\n\n /**\n * @throws BadRequest\n * @throws HubspotException\n */\n public function createMeeting(array $payload): Response\n {\n $endpoint = '/crm/v3/objects/meetings';\n\n return $this->makeRequest($endpoint, 'POST', $payload);\n }\n\n /**\n * @throws BadRequest\n * @throws HubspotException\n */\n public function updateMeeting(string $meetingId, array $payload): Response\n {\n $endpoint = '/crm/v3/objects/meetings/' . $meetingId;\n\n return $this->makeRequest($endpoint, 'PATCH', $payload);\n }\n\n /**\n * @throws \\Exception\n */\n public function createNote(\n string $body,\n string $ownerId,\n int $timestamp,\n string $objectId,\n NoteObject $noteObject\n ): ?string {\n try {\n $noteInput = new SimplePublicObjectInput([\n 'properties' => [\n 'hs_note_body' => $body,\n 'hubspot_owner_id' => $ownerId,\n 'hs_timestamp' => $timestamp,\n ],\n ]);\n\n // Create note\n $note = $this->getNewInstance()->crm()->objects()->basicApi()->create('note', $noteInput);\n\n $this->getNewInstance()->crm()->objects()->associationsApi()->create(\n 'note',\n $note->getId(),\n $this->getNoteObject($noteObject),\n $objectId,\n $this->getNoteAssociationType($noteObject),\n );\n\n return $note->getId();\n } catch (\\Exception $e) {\n $this->log->error('[Hubspot] Failed to create note', [\n 'objectId' => $objectId,\n 'noteObject' => $noteObject->getObjectType(),\n 'reason' => $e->getMessage(),\n ]);\n\n \\Sentry::captureException($e);\n }\n\n return null;\n }\n\n public function updateEngagement(string $objectId, array $engagement, array $metadata): void\n {\n $this->getInstance()->engagements()->update($objectId, $engagement, $metadata);\n }\n\n public function getEngagementData(string $engagementId): array\n {\n $engagement = $this->getInstance()->engagements()->get($engagementId);\n\n return $engagement->toArray();\n }\n\n public function createEngagement(array $engagement, array $associations, array $metadata): Response\n {\n return $this->getInstance()\n ->engagements()\n ->create($engagement, $associations, $metadata);\n }\n\n public function isUnauthorizedException(\\Exception $e): bool\n {\n // Check for specific HubSpot API exception types first\n if ($e instanceof BadRequest) {\n // BadRequest can contain 401 status codes\n return $e->getCode() === 401;\n }\n\n // Check for HTTP client exceptions with status codes\n if ($e instanceof \\GuzzleHttp\\Exception\\RequestException && $e->hasResponse()) {\n $response = $e->getResponse();\n if ($response !== null) {\n return $response->getStatusCode() === 401;\n }\n }\n\n // Check for Guzzle HTTP exceptions\n if ($e instanceof \\GuzzleHttp\\Exception\\ClientException) {\n return $e->getCode() === 401;\n }\n\n // Fallback to string matching as last resort, but be more specific\n $message = strtolower($e->getMessage());\n\n return str_contains($message, '401 unauthorized') ||\n str_contains($message, 'http 401') ||\n str_contains($message, 'status code 401') ||\n (preg_match('/\\b401\\b/', $message) && str_contains($message, 'unauthorized'));\n }\n\n /**\n * Validates and refreshes the access token if needed before API requests.\n * This ensures long-running processes don't fail due to token expiration.\n *\n * @throws SocialAccountTokenInvalidException\n */\n public function ensureValidToken(): void\n {\n if ($this->oauthAccount === null) {\n return;\n }\n\n $newToken = $this->tokenManager->ensureValidToken($this->oauthAccount);\n if ($newToken !== null) {\n $this->accessToken = $newToken;\n }\n }\n\n public function getConfig()\n {\n return $this->config;\n }\n\n // returns only active (archived=false)\n public function getOwners(): array\n {\n return $this->getNewInstance()->crm()->owners()->getAll();\n }\n\n /**\n * @param bool $archived\n *\n * @return array<Owner>|[]\n */\n public function getOwnersArchived(bool $archived = true): array\n {\n $endpoint = '/crm/v3/owners';\n $queryParams = [\n 'archived' => $archived ? 'true' : 'false',\n ];\n $queryString = http_build_query($queryParams);\n\n $owners = [];\n\n try {\n $response = $this->makeRequest(endpoint: $endpoint, queryString: $queryString);\n $responseData = $response?->toArray();\n\n foreach ($responseData['results'] as $result) {\n try {\n $owners[] = Owner::create($result);\n } catch (Throwable $e) {\n $this->log->error('[HubSpot] Failed to process owner data', [\n 'result' => $result,\n 'error' => $e->getMessage(),\n ]);\n\n continue;\n }\n }\n } catch (Throwable $e) {\n $this->log->error('HubSpot] Failed to fetch owners', [\n 'archived' => $archived,\n 'error' => $e->getMessage(),\n ]);\n\n return [];\n }\n\n return $owners;\n }\n\n public function getMeeting(string $engagementId): ObjectWithAssociations\n {\n return $this->getNewInstance()->crm()->objects()->basicApi()\n ->getById('meeting', $engagementId, null, 'contact,company,deal');\n }\n\n public function deleteEngagement(string $engagementId): void\n {\n $this->getInstance()->engagements()->delete((int) $engagementId);\n }\n\n public function getAssociationsData(array $ids, string $fromObject, string $toObject): array\n {\n $associationData = [];\n $idChunks = array_chunk($ids, self::ASSOCIATIONS_BATCH_SIZE_LIMIT);\n\n foreach ($idChunks as $idChunk) {\n try {\n $batchInput = new \\HubSpot\\Client\\Crm\\Associations\\Model\\BatchInputPublicObjectId();\n $batchInput->setInputs(array_map(function ($id) {\n $publicObjectId = new \\HubSpot\\Client\\Crm\\Associations\\Model\\PublicObjectId();\n $publicObjectId->setId($id);\n\n return $publicObjectId;\n }, $idChunk));\n\n $associatedObjectsData = $this\n ->getNewInstance()\n ->crm()\n ->associations()\n ->batchApi()\n ->read($fromObject, $toObject, $batchInput);\n\n if ($associatedObjectsData instanceof \\HubSpot\\Client\\Crm\\Associations\\Model\\BatchResponsePublicAssociationMulti) {\n foreach ($associatedObjectsData->getResults() as $association) {\n $from = $association->getFrom()->getId();\n $toAssociations = $association->getTo();\n\n if (! empty($toAssociations)) {\n $associationData[$from] = array_map(function ($item) {\n return $item->getId();\n }, $toAssociations);\n }\n }\n }\n// } catch (RateLimitException $e) {\n// throw $e;\n } catch (\\Exception $e) {\n $this->log->error('[Hubspot] Failed to fetch associations', [\n 'from_object' => $fromObject,\n 'to_object' => $toObject,\n 'reason' => $e->getMessage(),\n ]);\n }\n }\n\n return $associationData;\n }\n\n /**\n * @throws \\Exception\n */\n private function getNoteAssociationType(NoteObject $noteObject): string\n {\n return match($noteObject) {\n NoteObject::Opportunity => 'note_to_deal',\n NoteObject::Lead, NoteObject::Contact => 'note_to_contact', // or 'note_to_lead' if your portal supports it\n NoteObject::Account => 'note_to_company',\n NoteObject::Call, NoteObject::Event => throw new \\Exception('Not supported'),\n };\n }\n\n /**\n * @throws \\Exception\n */\n private function getNoteObject(NoteObject $noteObject): string\n {\n return match($noteObject) {\n NoteObject::Opportunity => 'deal',\n NoteObject::Lead, NoteObject::Contact => 'contact',\n NoteObject::Account => 'company',\n NoteObject::Call, NoteObject::Event => throw new \\Exception('Not supported'),\n };\n }\n\n public function addAssociations(string $objectType, string $associationType, array $payload): Response\n {\n $endpoint = \"/crm/v4/associations/$objectType/$associationType/batch/create\";\n\n return $this->makeRequest($endpoint, 'POST', $payload);\n }\n\n public function removeAssociations(string $objectType, string $associationType, array $payload): Response\n {\n $endpoint = \"/crm/v4/associations/$objectType/$associationType/batch/archive\";\n\n return $this->makeRequest($endpoint, 'POST', $payload);\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Services\\Crm\\Hubspot;\n\nuse HubSpot\\Client\\Crm\\Deals\\ApiException as DealApiException;\nuse HubSpot\\Client\\Crm\\Contacts\\ApiException as ContactApiException;\nuse HubSpot\\Client\\Crm\\Companies\\ApiException as CompanyApiException;\nuse HubSpot\\Client\\Crm\\Contacts\\Model\\SimplePublicObjectWithAssociations as ContactsWithAssociations;\nuse HubSpot\\Client\\Crm\\Companies\\Model\\SimplePublicObjectWithAssociations as CompaniesWithAssociations;\nuse HubSpot\\Client\\Crm\\Deals\\Model\\SimplePublicObjectWithAssociations as DealWithAssociations;\nuse HubSpot\\Client\\Crm\\Objects\\Model\\SimplePublicObjectInput;\nuse HubSpot\\Client\\Crm\\Objects\\Model\\SimplePublicObjectWithAssociations as ObjectWithAssociations;\nuse HubSpot\\Client\\Crm\\Pipelines\\Model\\Error;\nuse HubSpot\\Client\\Crm\\Pipelines\\Model\\PipelineStage;\nuse HubSpot\\Client\\Crm\\Properties\\Model\\Property;\nuse HubSpot\\Discovery\\Discovery;\nuse Jiminny\\Exceptions\\CrmException;\nuse Jiminny\\Exceptions\\RateLimitException;\nuse Jiminny\\Exceptions\\SocialAccountTokenInvalidException;\nuse Jiminny\\Jobs\\Crm\\NoteObject;\nuse Jiminny\\Models\\Crm\\Field;\nuse Jiminny\\Services\\Crm\\BaseClient;\nuse Jiminny\\Services\\Crm\\Hubspot\\DTO\\Response\\Owner;\nuse Jiminny\\Services\\SocialAccountService;\nuse SevenShores\\Hubspot\\Exceptions\\BadRequest;\nuse SevenShores\\Hubspot\\Exceptions\\HubspotException;\nuse SevenShores\\Hubspot\\Factory;\nuse SevenShores\\Hubspot\\Http\\Response;\nuse Jiminny\\Services\\Crm\\Hubspot\\Pagination\\HubspotPaginationService;\nuse Illuminate\\Support\\Facades\\Redis;\nuse Throwable;\n\n/**\n * @phpstan-type CrmFieldOption array{id:string, label:string, value?:string}\n */\nclass Client extends BaseClient implements HubspotClientInterface\n{\n public const string MIN_API_VERSION = '2';\n\n public const string BASE_URL = 'https://api.hubapi.com';\n\n public const int ASSOCIATIONS_BATCH_SIZE_LIMIT = 1000;\n\n private HubspotPaginationService $paginationService;\n private HubspotTokenManager $tokenManager;\n\n public function __construct(\n SocialAccountService $socialAccountService,\n HubspotPaginationService $paginationService,\n HubspotTokenManager $tokenManager\n ) {\n parent::__construct($socialAccountService);\n $this->paginationService = $paginationService;\n $this->tokenManager = $tokenManager;\n\n $this->setBaseUrl(self::BASE_URL);\n $this->setVersion(self::MIN_API_VERSION);\n }\n\n /**\n * Reacts to a rate limits (429) from HubSpot by translating it\n * into a RateLimitException carrying retry_after.\n *\n * Wrap any outbound HubSpot call (SDK or raw HTTP) like:\n *\n * $this->executeRequest(fn () => $this->getNewInstance()->crm()->...);\n *\n * @template T\n * @param callable(): T $apiCall\n * @return T\n *\n * @throws RateLimitException\n */\n private function executeRequest(callable $apiCall)\n {\n $cacheKey = $this->getRateLimitCacheKey();\n\n $cachedRetryAfter = Redis::get($cacheKey);\n if (is_string($cachedRetryAfter) && is_numeric($cachedRetryAfter)) {\n throw new RateLimitException(\n 'Hubspot rate limit (cached circuit-breaker)',\n (int) $cachedRetryAfter,\n );\n }\n\n try {\n return $apiCall();\n } catch (Throwable $e) {\n if ($this->isHubspotRateLimit($e)) {\n $retryAfter = $this->parseRetryAfter($e);\n\n Redis::setex($cacheKey, $retryAfter, (string) $retryAfter);\n\n $this->log->warning('[Hubspot] Received 429 from API', [\n 'team_id' => $this->config->team_id,\n 'config_id' => $this->config->getId(),\n 'retry_after' => $retryAfter,\n 'message' => $this->parseMessage($e),\n 'reason' => $e->getMessage(),\n ]);\n\n throw new RateLimitException('Hubspot returned 429', $retryAfter, $e);\n }\n\n throw $e;\n }\n }\n\n private function getRateLimitCacheKey(): string\n {\n return sprintf('hubspot:ratelimit:portal:%d', $this->config->getId());\n }\n\n public function isHubspotRateLimit(Throwable $e): bool\n {\n if ($e instanceof BadRequest\n || $e instanceof DealApiException\n || $e instanceof ContactApiException\n || $e instanceof CompanyApiException\n || $e instanceof \\GuzzleHttp\\Exception\\RequestException\n ) {\n return (int) $e->getCode() === 429;\n }\n\n return false;\n }\n\n public function parseRetryAfter(Throwable $e): int\n {\n if (method_exists($e, 'getResponseHeaders')) {\n $headers = $e->getResponseHeaders() ?: [];\n $value = $headers['Retry-After'] ?? $headers['retry-after'] ?? null;\n if (is_array($value)) {\n $value = $value[0] ?? null;\n }\n if (is_numeric($value)) {\n return (int) $value;\n }\n }\n\n $message = strtolower($this->parseMessage($e));\n\n if (str_contains($message, 'daily')) {\n return 600;\n }\n if (str_contains($message, 'ten secondly')) {\n return 10;\n }\n if (str_contains($message, 'secondly')) {\n return 1;\n }\n\n $this->log->warning('[Hubspot] No retry-after header or known message, using default', [\n 'exception_class' => get_class($e),\n 'message' => $message,\n ]);\n\n return 10;\n }\n\n public function parseMessage(Throwable $e): string\n {\n if (! method_exists($e, 'getResponseBody')) {\n return '';\n }\n\n $body = $e->getResponseBody();\n if (is_string($body)) {\n $body = json_decode($body, true) ?? [];\n }\n\n if (! is_array($body)) {\n return '';\n }\n\n return (string) ($body['message'] ?? '');\n }\n\n public function getMinimumApiVersion(): string\n {\n return self::MIN_API_VERSION;\n }\n\n public function getInstance(): Factory\n {\n return new Factory([\n 'key' => $this->accessToken,\n 'oauth2' => true,\n 'base_url' => $this->baseUrl,\n ]);\n }\n\n public function getNewInstance(): Discovery\n {\n return \\HubSpot\\Factory::createWithAccessToken($this->accessToken);\n }\n\n /**\n * Secondly and daily limits for Hubspot API\n *\n * Product Tier: Free & Starter | Professional & Enterprise | API add-on (any tier)\n * Burst: 100/10 seconds | 150/10 seconds | 200/10 seconds\n * Daily: 250,000 | 500,000 | 1,000,000\n *\n * Official documentation states: The search endpoints are rate limited to five requests per second.\n * Since with 5 RPS were still hitting secondly rate limits we lowered it to 4\n */\n public function getPaginatedData(array $payload, string $type, int $offset = 0): array\n {\n $total = 0;\n $lastId = null;\n $rows = [];\n foreach ($this->getPaginatedDataGenerator($payload, $type, $offset, $total, $lastId) as $row) {\n $rows[] = $row;\n }\n\n return ['results' => $rows, 'total' => $total, 'last_record' => $lastId];\n }\n\n /**\n * @throws HubspotException\n * @throws SocialAccountTokenInvalidException\n * @throws BadRequest\n */\n public function getPaginatedDataGenerator(\n array $payload,\n string $type,\n int $offset = 0,\n int &$total = 0,\n ?string &$lastRecordId = null\n ): \\Generator {\n return $this->paginationService->getPaginatedDataGenerator(\n $this,\n $payload,\n $type,\n $offset,\n $total,\n $lastRecordId\n );\n }\n\n /**\n * Execute a search request against HubSpot CRM objects with rate limiting.\n *\n * @param string $objectType The object type ('deals', 'companies', 'contacts', 'calls')\n * @param array<string, mixed> $payload The search payload with filters, sorts, properties, etc.\n * @return array The search response with 'results', 'total', 'paging' keys\n * @throws RateLimitException When rate limit is hit\n * @throws HubspotException On API errors\n */\n public function search(string $objectType, array $payload): array\n {\n $endpoint = self::BASE_URL . \"/crm/v3/objects/{$objectType}/search\";\n\n return $this->executeRequest(function () use ($endpoint, $payload) {\n $response = $this->getInstance()->getClient()->request('POST', $endpoint, ['json' => $payload]);\n\n return $response->toArray();\n });\n }\n\n /**\n * @throws DealApiException\n * @throws CrmException\n */\n public function getOpportunityById(string $crmId, array $fields): array\n {\n try {\n// $deal = $this->executeRequest(fn () => $this->getNewInstance()->crm()->deals()->basicApi()->getById(\n $deal = $this->getNewInstance()->crm()->deals()->basicApi()->getById(\n $crmId,\n implode(',', $fields),\n 'companies,contacts'\n );\n } catch (DealApiException $e) {\n $this->log->info('[Hubspot] Failed to fetch opportunity', [\n 'crm_id' => $crmId,\n 'reason' => $e->getMessage(),\n ]);\n\n throw $e;\n }\n\n if (! $deal instanceof DealWithAssociations) {\n throw new CrmException('Deal not found');\n }\n\n return [\n 'id' => $deal->getId(),\n 'properties' => $deal->getProperties(),\n 'associations' => $deal->getAssociations(),\n ];\n }\n\n /**\n * Generic batch read method for HubSpot objects\n *\n * @param string $objectType The object type ('deals', 'companies', 'contacts')\n * @param array<string> $crmIds Array of HubSpot object IDs (max 100)\n * @param array<string> $fields Array of property names to fetch\n *\n * @return array<string, array> Array keyed by CRM ID with object data\n */\n private function batchReadObjects(string $objectType, array $crmIds, array $fields): array\n {\n if (empty($crmIds)) {\n return [];\n }\n\n $this->validateBatchSize($objectType, $crmIds);\n $this->ensureValidToken();\n\n try {\n $batchConfig = $this->createBatchConfiguration($objectType);\n $batchReadRequest = $this->prepareBatchRequest($batchConfig, $crmIds, $fields);\n $response = $batchConfig['api']->read($batchReadRequest);\n\n $this->validateApiResponse($response, $objectType);\n\n $results = $this->processApiResults($response);\n $this->logBatchResults($objectType, $crmIds, $results);\n\n return $results;\n } catch (\\Throwable $e) {\n $this->handleBatchError($e, $objectType, $crmIds);\n }\n }\n\n private function validateBatchSize(string $objectType, array $crmIds): void\n {\n if (count($crmIds) > 100) {\n throw new \\InvalidArgumentException(\"Batch size cannot exceed 100 {$objectType}\");\n }\n }\n\n private function createBatchConfiguration(string $objectType): array\n {\n $configurations = [\n 'deals' => [\n 'batchReadRequest' => new \\HubSpot\\Client\\Crm\\Deals\\Model\\BatchReadInputSimplePublicObjectId(),\n 'inputClass' => \\HubSpot\\Client\\Crm\\Deals\\Model\\SimplePublicObjectId::class,\n 'api' => $this->getNewInstance()->crm()->deals()->batchApi(),\n ],\n 'companies' => [\n 'batchReadRequest' => new \\HubSpot\\Client\\Crm\\Companies\\Model\\BatchReadInputSimplePublicObjectId(),\n 'inputClass' => \\HubSpot\\Client\\Crm\\Companies\\Model\\SimplePublicObjectId::class,\n 'api' => $this->getNewInstance()->crm()->companies()->batchApi(),\n ],\n 'contacts' => [\n 'batchReadRequest' => new \\HubSpot\\Client\\Crm\\Contacts\\Model\\BatchReadInputSimplePublicObjectId(),\n 'inputClass' => \\HubSpot\\Client\\Crm\\Contacts\\Model\\SimplePublicObjectId::class,\n 'api' => $this->getNewInstance()->crm()->contacts()->batchApi(),\n ],\n ];\n\n if (! isset($configurations[$objectType])) {\n throw new \\InvalidArgumentException(\"Unsupported object type: {$objectType}\");\n }\n\n return $configurations[$objectType];\n }\n\n private function prepareBatchRequest(array $batchConfig, array $crmIds, array $fields): object\n {\n $batchReadRequest = $batchConfig['batchReadRequest'];\n $inputClass = $batchConfig['inputClass'];\n\n $inputs = array_map(function ($crmId) use ($inputClass) {\n $input = new $inputClass();\n $input->setId($crmId);\n\n return $input;\n }, $crmIds);\n\n $batchReadRequest->setInputs($inputs);\n $batchReadRequest->setProperties($fields);\n\n return $batchReadRequest;\n }\n\n private function validateApiResponse($response, string $objectType): void\n {\n if (! $response) {\n throw new CrmException(\"HubSpot API returned null response for {$objectType} batch read\");\n }\n }\n\n private function processApiResults($response): array\n {\n $results = [];\n $responseResults = $response->getResults();\n\n if ($responseResults) {\n foreach ($responseResults as $object) {\n if ($object && $object->getId()) {\n $results[$object->getId()] = [\n 'id' => $object->getId(),\n 'properties' => $object->getProperties() ?: [],\n ];\n }\n }\n }\n\n return $results;\n }\n\n private function logBatchResults(string $objectType, array $crmIds, array $results): void\n {\n $this->log->info(\"[HubSpot] Batch fetched {$objectType}\", [\n 'requested_count' => count($crmIds),\n 'returned_count' => count($results),\n 'crm_ids' => $crmIds,\n ]);\n }\n\n private function handleBatchError(\\Throwable $e, string $objectType, array $crmIds): void\n {\n $errorMessage = $e->getMessage() ?: 'Unknown error';\n $errorTrace = $e->getTraceAsString() ?: 'No trace available';\n\n $this->log->error(\"[HubSpot] Failed to batch fetch {$objectType}\", [\n 'crm_ids' => $crmIds,\n 'error' => $errorMessage,\n 'trace' => $errorTrace,\n ]);\n\n throw new CrmException(\"Failed to batch fetch {$objectType}: \" . $errorMessage);\n }\n\n /**\n * Batch read multiple opportunities by their CRM IDs\n *\n * @param array<string> $crmIds Array of HubSpot deal IDs (max 100)\n * @param array<string> $fields Array of property names to fetch\n *\n * @return array<string, array> Array keyed by CRM ID with opportunity data\n */\n public function getOpportunitiesByIds(array $crmIds, array $fields): array\n {\n return $this->batchReadObjects('deals', $crmIds, $fields);\n }\n\n /**\n * Batch read multiple companies by their CRM IDs\n *\n * @param array<string> $crmIds Array of HubSpot company IDs (max 100)\n * @param array<string> $fields Array of property names to fetch\n *\n * @return array<string, array> Array keyed by CRM ID with company data\n */\n public function getCompaniesByIds(array $crmIds, array $fields): array\n {\n return $this->batchReadObjects('companies', $crmIds, $fields);\n }\n\n /**\n * Batch read multiple contacts by their CRM IDs\n *\n * @param array<string> $crmIds Array of HubSpot contact IDs (max 100)\n * @param array<string> $fields Array of property names to fetch\n *\n * @return array<string, array> Array keyed by CRM ID with contact data\n */\n public function getContactsByIds(array $crmIds, array $fields): array\n {\n return $this->batchReadObjects('contacts', $crmIds, $fields);\n }\n\n /**\n * @throws CompanyApiException\n * @throws CrmException\n */\n public function getAccountById(string $crmId, array $fields): array\n {\n try {\n $company = $this->getNewInstance()->crm()->companies()->basicApi()->getById(\n $crmId,\n implode(',', $fields),\n );\n } catch (CompanyApiException $e) {\n $this->log->info('[Hubspot] Failed to fetch account', [\n 'crm_id' => $crmId,\n 'reason' => $e->getMessage(),\n ]);\n\n throw $e;\n }\n\n if (! $company instanceof CompaniesWithAssociations) {\n throw new CrmException('Account not found');\n }\n\n return [\n 'id' => $company->getId(),\n 'properties' => $company->getProperties(),\n ];\n }\n\n /**\n * @throws ContactApiException\n * @throws CrmException\n */\n public function getContactById(string $crmId, array $fields): array\n {\n try {\n $contact = $this->getNewInstance()->crm()->contacts()->basicApi()->getById(\n $crmId,\n implode(',', $fields)\n );\n } catch (ContactApiException $e) {\n $this->log->info('[Hubspot] Failed to fetch contact', [\n 'crm_id' => $crmId,\n 'reason' => $e->getMessage(),\n ]);\n\n throw $e;\n }\n\n if (! $contact instanceof ContactsWithAssociations) {\n throw new CrmException('Contact not found');\n }\n\n return [\n 'id' => $contact->getId(),\n 'properties' => $contact->getProperties(),\n ];\n }\n\n /**\n * This is email search request that Hubspot offers as GET (more generous quota)\n */\n public function getContactByEmail(string $email, array $fields = []): array\n {\n try {\n $contact = $this->getNewInstance()->crm()->contacts()->basicApi()->getById(\n $email,\n implode(',', $fields),\n null,\n false,\n 'email'\n );\n\n return [\n 'id' => $contact->getId(),\n 'properties' => $contact->getProperties(),\n ];\n } catch (ContactApiException $e) {\n $this->log->info('[Hubspot] Failed to fetch contact', [\n 'email' => $email,\n 'reason' => $e->getMessage(),\n ]);\n\n return [];\n }\n }\n\n /**\n * @throws CrmException\n */\n public function fetchProperty(string $objectType, string $propertyId): Property\n {\n $result = $this->getNewInstance()->crm()->properties()->coreApi()->getByName($objectType, $propertyId);\n\n if (! $result instanceof Property) {\n $this->log->error('[Hubspot] Failed to fetch property', [\n 'object_type' => $objectType,\n 'property_id' => $propertyId,\n 'reason' => $result->getMessage(),\n ]);\n\n throw new CrmException('Failed to fetch property');\n }\n\n return $result;\n }\n\n /**\n * @return array<CrmFieldOption>\n */\n public function fetchPropertyOptions(string $objectType, string $propertyId): array\n {\n /** @var array<CrmFieldOption> */\n return $this->fetchProperty($objectType, $propertyId)->getOptions();\n }\n\n /**\n * @return array<array{id:string, label:string, deleted:bool}>\n */\n public function fetchCallDispositions(): array\n {\n /** @var Response $response */\n $response = $this->getInstance()->engagements()->getCallDispositions();\n\n /**\n * @var array<array{\n * id:string,\n * label:string,\n * deleted: bool\n * }>\n */\n return $response->toArray();\n }\n\n /**\n * @return array<CrmFieldOption>\n */\n public function fetchOpportunityPipelineStages(): array\n {\n $stages = [];\n $apiResponse = $this->getNewInstance()->crm()->pipelines()->pipelinesApi()->getAll('deals');\n\n if ($apiResponse instanceof Error) {\n $this->log->error('[Hubspot] Failed to fetch opportunity pipelines', [\n 'reason' => $apiResponse->getMessage(),\n ]);\n\n return [];\n }\n\n foreach ($apiResponse->getResults() as $pipeline) {\n $pipelineStages = array_map(\n static function (PipelineStage $stage) {\n return [\n 'id' => $stage->getId(),\n 'label' => $stage->getLabel(),\n ];\n },\n $pipeline->getStages()\n );\n\n $stages = array_merge($stages, $pipelineStages);\n }\n\n return $stages;\n }\n\n public function fetchOpportunityPipelines(): array\n {\n $pipelines = [];\n\n try {\n $apiResponse = $this->makeRequest('/crm/v3/pipelines/deals');\n } catch (\\Exception $e) {\n $this->log->info('[Hubspot] Failed to fetch opportunity pipelines', [\n 'reason' => $e->getMessage(),\n ]);\n\n return [];\n }\n\n $response = $apiResponse->toArray();\n\n foreach ($response['results'] as $pipeline) {\n $pipelines[] = [\n 'id' => $pipeline['id'],\n 'label' => $pipeline['label'],\n ];\n }\n\n return $pipelines;\n }\n\n /**\n * @return array<CrmFieldOption>\n */\n public function fetchMeetingOutcomeFieldOptions(Field $field): array\n {\n return $field->getCrmProviderId() === 'meetingOutcome'\n ? $this->fetchMeetingOutcomeTypes()\n : $this->fetchCallActivityTypes();\n }\n\n public function fetchMeetingOutcomeTypes(): array\n {\n return $this->extractMeetingTypeOptions(\n 'https://api.hubapi.com/crm/v3/properties/meeting/hs_meeting_outcome'\n );\n }\n\n public function fetchCallActivityTypes(): array\n {\n return $this->extractMeetingTypeOptions(\n 'https://api.hubapi.com/crm/v3/properties/call/hs_activity_type'\n );\n }\n\n private function extractMeetingTypeOptions(string $endpoint): array\n {\n /** @var Response $response */\n $response = $this->getInstance()\n ->getClient()\n ->request('GET', $endpoint);\n\n /**\n * @var array<array{\n * value: string,\n * label: string,\n * displayOrder: int\n * }> $optionData\n */\n $optionData = $response->toArray()['options'] ?? [];\n\n $options = [];\n foreach ($optionData as $item) {\n $options[] = [\n 'id' => $item['value'],\n 'value' => $item['value'],\n 'label' => $item['label'],\n 'display_order' => $item['displayOrder'],\n ];\n }\n\n return $options;\n }\n\n /**\n * @return array<CrmFieldOption>\n */\n public function fetchDispositionFieldOptions(): array\n {\n $options = [];\n\n $dispositions = $this->fetchCallDispositions();\n\n foreach ($dispositions as $disposition) {\n if ($disposition['deleted'] !== false) {\n continue;\n }\n\n $option['value'] = $disposition['id'];\n $option['id'] = $disposition['id'];\n $option['label'] = $disposition['label'];\n\n $options[] = $option;\n }\n\n return $options;\n }\n\n /**\n * @return array<CrmFieldOption>\n */\n public function fetchOpportunityFieldOptions(Field $field): array\n {\n if ($field->isStageField()) {\n return $this->fetchOpportunityPipelineStages();\n }\n\n if ($field->isPipelineField()) {\n return $this->fetchOpportunityPipelines();\n }\n\n return $this->fetchPropertyOptions('deals', $field->getCrmProviderId());\n }\n\n /**\n * @throws BadRequest\n * @throws HubspotException\n */\n public function makeRequest(string $endpoint, $method = 'GET', $payload = [], ?string $queryString = null)\n {\n $endpoint = self::BASE_URL . $endpoint;\n\n if ($method === 'GET') {\n return $this->getInstance()->getClient()?->request(\n method: $method,\n endpoint: $endpoint,\n query_string: $queryString\n );\n } else {\n return $this->getInstance()->getClient()->request($method, $endpoint, [\n 'json' => ($payload),\n ]);\n }\n }\n\n /**\n * @throws BadRequest\n * @throws HubspotException\n */\n public function createMeeting(array $payload): Response\n {\n $endpoint = '/crm/v3/objects/meetings';\n\n return $this->makeRequest($endpoint, 'POST', $payload);\n }\n\n /**\n * @throws BadRequest\n * @throws HubspotException\n */\n public function updateMeeting(string $meetingId, array $payload): Response\n {\n $endpoint = '/crm/v3/objects/meetings/' . $meetingId;\n\n return $this->makeRequest($endpoint, 'PATCH', $payload);\n }\n\n /**\n * @throws \\Exception\n */\n public function createNote(\n string $body,\n string $ownerId,\n int $timestamp,\n string $objectId,\n NoteObject $noteObject\n ): ?string {\n try {\n $noteInput = new SimplePublicObjectInput([\n 'properties' => [\n 'hs_note_body' => $body,\n 'hubspot_owner_id' => $ownerId,\n 'hs_timestamp' => $timestamp,\n ],\n ]);\n\n // Create note\n $note = $this->getNewInstance()->crm()->objects()->basicApi()->create('note', $noteInput);\n\n $this->getNewInstance()->crm()->objects()->associationsApi()->create(\n 'note',\n $note->getId(),\n $this->getNoteObject($noteObject),\n $objectId,\n $this->getNoteAssociationType($noteObject),\n );\n\n return $note->getId();\n } catch (\\Exception $e) {\n $this->log->error('[Hubspot] Failed to create note', [\n 'objectId' => $objectId,\n 'noteObject' => $noteObject->getObjectType(),\n 'reason' => $e->getMessage(),\n ]);\n\n \\Sentry::captureException($e);\n }\n\n return null;\n }\n\n public function updateEngagement(string $objectId, array $engagement, array $metadata): void\n {\n $this->getInstance()->engagements()->update($objectId, $engagement, $metadata);\n }\n\n public function getEngagementData(string $engagementId): array\n {\n $engagement = $this->getInstance()->engagements()->get($engagementId);\n\n return $engagement->toArray();\n }\n\n public function createEngagement(array $engagement, array $associations, array $metadata): Response\n {\n return $this->getInstance()\n ->engagements()\n ->create($engagement, $associations, $metadata);\n }\n\n public function isUnauthorizedException(\\Exception $e): bool\n {\n // Check for specific HubSpot API exception types first\n if ($e instanceof BadRequest) {\n // BadRequest can contain 401 status codes\n return $e->getCode() === 401;\n }\n\n // Check for HTTP client exceptions with status codes\n if ($e instanceof \\GuzzleHttp\\Exception\\RequestException && $e->hasResponse()) {\n $response = $e->getResponse();\n if ($response !== null) {\n return $response->getStatusCode() === 401;\n }\n }\n\n // Check for Guzzle HTTP exceptions\n if ($e instanceof \\GuzzleHttp\\Exception\\ClientException) {\n return $e->getCode() === 401;\n }\n\n // Fallback to string matching as last resort, but be more specific\n $message = strtolower($e->getMessage());\n\n return str_contains($message, '401 unauthorized') ||\n str_contains($message, 'http 401') ||\n str_contains($message, 'status code 401') ||\n (preg_match('/\\b401\\b/', $message) && str_contains($message, 'unauthorized'));\n }\n\n /**\n * Validates and refreshes the access token if needed before API requests.\n * This ensures long-running processes don't fail due to token expiration.\n *\n * @throws SocialAccountTokenInvalidException\n */\n public function ensureValidToken(): void\n {\n if ($this->oauthAccount === null) {\n return;\n }\n\n $newToken = $this->tokenManager->ensureValidToken($this->oauthAccount);\n if ($newToken !== null) {\n $this->accessToken = $newToken;\n }\n }\n\n public function getConfig()\n {\n return $this->config;\n }\n\n // returns only active (archived=false)\n public function getOwners(): array\n {\n return $this->getNewInstance()->crm()->owners()->getAll();\n }\n\n /**\n * @param bool $archived\n *\n * @return array<Owner>|[]\n */\n public function getOwnersArchived(bool $archived = true): array\n {\n $endpoint = '/crm/v3/owners';\n $queryParams = [\n 'archived' => $archived ? 'true' : 'false',\n ];\n $queryString = http_build_query($queryParams);\n\n $owners = [];\n\n try {\n $response = $this->makeRequest(endpoint: $endpoint, queryString: $queryString);\n $responseData = $response?->toArray();\n\n foreach ($responseData['results'] as $result) {\n try {\n $owners[] = Owner::create($result);\n } catch (Throwable $e) {\n $this->log->error('[HubSpot] Failed to process owner data', [\n 'result' => $result,\n 'error' => $e->getMessage(),\n ]);\n\n continue;\n }\n }\n } catch (Throwable $e) {\n $this->log->error('HubSpot] Failed to fetch owners', [\n 'archived' => $archived,\n 'error' => $e->getMessage(),\n ]);\n\n return [];\n }\n\n return $owners;\n }\n\n public function getMeeting(string $engagementId): ObjectWithAssociations\n {\n return $this->getNewInstance()->crm()->objects()->basicApi()\n ->getById('meeting', $engagementId, null, 'contact,company,deal');\n }\n\n public function deleteEngagement(string $engagementId): void\n {\n $this->getInstance()->engagements()->delete((int) $engagementId);\n }\n\n public function getAssociationsData(array $ids, string $fromObject, string $toObject): array\n {\n $associationData = [];\n $idChunks = array_chunk($ids, self::ASSOCIATIONS_BATCH_SIZE_LIMIT);\n\n foreach ($idChunks as $idChunk) {\n try {\n $batchInput = new \\HubSpot\\Client\\Crm\\Associations\\Model\\BatchInputPublicObjectId();\n $batchInput->setInputs(array_map(function ($id) {\n $publicObjectId = new \\HubSpot\\Client\\Crm\\Associations\\Model\\PublicObjectId();\n $publicObjectId->setId($id);\n\n return $publicObjectId;\n }, $idChunk));\n\n $associatedObjectsData = $this\n ->getNewInstance()\n ->crm()\n ->associations()\n ->batchApi()\n ->read($fromObject, $toObject, $batchInput);\n\n if ($associatedObjectsData instanceof \\HubSpot\\Client\\Crm\\Associations\\Model\\BatchResponsePublicAssociationMulti) {\n foreach ($associatedObjectsData->getResults() as $association) {\n $from = $association->getFrom()->getId();\n $toAssociations = $association->getTo();\n\n if (! empty($toAssociations)) {\n $associationData[$from] = array_map(function ($item) {\n return $item->getId();\n }, $toAssociations);\n }\n }\n }\n// } catch (RateLimitException $e) {\n// throw $e;\n } catch (\\Exception $e) {\n $this->log->error('[Hubspot] Failed to fetch associations', [\n 'from_object' => $fromObject,\n 'to_object' => $toObject,\n 'reason' => $e->getMessage(),\n ]);\n }\n }\n\n return $associationData;\n }\n\n /**\n * @throws \\Exception\n */\n private function getNoteAssociationType(NoteObject $noteObject): string\n {\n return match($noteObject) {\n NoteObject::Opportunity => 'note_to_deal',\n NoteObject::Lead, NoteObject::Contact => 'note_to_contact', // or 'note_to_lead' if your portal supports it\n NoteObject::Account => 'note_to_company',\n NoteObject::Call, NoteObject::Event => throw new \\Exception('Not supported'),\n };\n }\n\n /**\n * @throws \\Exception\n */\n private function getNoteObject(NoteObject $noteObject): string\n {\n return match($noteObject) {\n NoteObject::Opportunity => 'deal',\n NoteObject::Lead, NoteObject::Contact => 'contact',\n NoteObject::Account => 'company',\n NoteObject::Call, NoteObject::Event => throw new \\Exception('Not supported'),\n };\n }\n\n public function addAssociations(string $objectType, string $associationType, array $payload): Response\n {\n $endpoint = \"/crm/v4/associations/$objectType/$associationType/batch/create\";\n\n return $this->makeRequest($endpoint, 'POST', $payload);\n }\n\n public function removeAssociations(string $objectType, string $associationType, array $payload): Response\n {\n $endpoint = \"/crm/v4/associations/$objectType/$associationType/batch/archive\";\n\n return $this->makeRequest($endpoint, 'POST', $payload);\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":true,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"19","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"[2026-05-07 14:21:15] local.INFO: [Hubspot] DEBUG Getting headers {\n\"headers\":{\n\"Date\":[\"Thu,07 May 2026 14:21:15 GMT\"],\n \"Content-Type\":[\"application/json;charset=utf-8\"],\n \"Transfer-Encoding\":[\"chunked\"],\n \"Connection\":[\"keep-alive\"],\n \"CF-Ray\":[\"9f80deb8db60dc3a-SOF\"],\n \"CF-Cache-Status\":[\"DYNAMIC\"],\n \"Strict-Transport-Security\":[\"max-age=31536000; includeSubDomains; preload\"],\n \"Vary\":[\"origin,\n accept-encoding\"],\n \"access-control-allow-credentials\":[\"false\"],\n \"server-timing\":[\"hcid;desc=\\\"019e02d0-6fd8-7812-bdba-885b7ccb3ee3\\\",\n cfr;desc=\\\"9f80deb8e7c6dc3a-IAD\\\"\"],\n \"x-content-type-options\":[\"nosniff\"],\n \"x-hubspot-correlation-id\":[\"019e02d0-6fd8-7812-bdba-885b7ccb3ee3\"],\n \"Set-Cookie\":[\"__cf_bm=SIUrtdQgXVrik50pdqF6hZVYKhzTnQBidvMabeCtm0Y-1778163675-1.0.1.1-rI.ZggtDKxTge5zr8_2gbBfWMQQ.ufZEXDZyHz2mBUFdzdo2gTHEsOkXMSEShjK0hGYxNhUGM1ZoBpX7BcFZcHEjA7Cs_.SMUhUnd2nYjko; path=/; expires=Thu,\n 07-May-26 14:51:15 GMT; domain=.hubapi.com; HttpOnly; Secure; SameSite=None\"],\n \"Report-To\":[\"{\n\\\"endpoints\\\":[{\n\\\"url\\\":\\\"https:\\\\/\\\\/a.nel.cloudflare.com\\\\/report\\\\/v4?s=NYAlsVTP0fYm32qrSDjxYE4sd2RWRqiSp3wHsmdEgZlzoYdxI%2BIxVpHmsKn3O%2BKVA3mFIJ2m7YRECDGSM%2BW2IYTzo6FM4%2BdUIjURO8srzKSvJgZ%2BQ6R79arKQw3uHLlX\\\"}],\n\\\"group\\\":\\\"cf-nel\\\",\n\\\"max_age\\\":604800}\"],\n\"NEL\":[\"{\n\\\"success_fraction\\\":0.01,\n\\\"report_to\\\":\\\"cf-nel\\\",\n\\\"max_age\\\":604800}\"],\n\"Server\":[\"cloudflare\"]}} {\n\"correlation_id\":\"95236535-ec98-4541-b92a-adfa73b69eab\",\n\"trace_id\":\"c7ab8365-903f-46d4-9403-0e5b551e3545\"}","depth":4,"on_screen":true,"value":"[2026-05-07 14:21:15] local.INFO: [Hubspot] DEBUG Getting headers {\n\"headers\":{\n\"Date\":[\"Thu,07 May 2026 14:21:15 GMT\"],\n \"Content-Type\":[\"application/json;charset=utf-8\"],\n \"Transfer-Encoding\":[\"chunked\"],\n \"Connection\":[\"keep-alive\"],\n \"CF-Ray\":[\"9f80deb8db60dc3a-SOF\"],\n \"CF-Cache-Status\":[\"DYNAMIC\"],\n \"Strict-Transport-Security\":[\"max-age=31536000; includeSubDomains; preload\"],\n \"Vary\":[\"origin,\n accept-encoding\"],\n \"access-control-allow-credentials\":[\"false\"],\n \"server-timing\":[\"hcid;desc=\\\"019e02d0-6fd8-7812-bdba-885b7ccb3ee3\\\",\n cfr;desc=\\\"9f80deb8e7c6dc3a-IAD\\\"\"],\n \"x-content-type-options\":[\"nosniff\"],\n \"x-hubspot-correlation-id\":[\"019e02d0-6fd8-7812-bdba-885b7ccb3ee3\"],\n \"Set-Cookie\":[\"__cf_bm=SIUrtdQgXVrik50pdqF6hZVYKhzTnQBidvMabeCtm0Y-1778163675-1.0.1.1-rI.ZggtDKxTge5zr8_2gbBfWMQQ.ufZEXDZyHz2mBUFdzdo2gTHEsOkXMSEShjK0hGYxNhUGM1ZoBpX7BcFZcHEjA7Cs_.SMUhUnd2nYjko; path=/; expires=Thu,\n 07-May-26 14:51:15 GMT; domain=.hubapi.com; HttpOnly; Secure; SameSite=None\"],\n \"Report-To\":[\"{\n\\\"endpoints\\\":[{\n\\\"url\\\":\\\"https:\\\\/\\\\/a.nel.cloudflare.com\\\\/report\\\\/v4?s=NYAlsVTP0fYm32qrSDjxYE4sd2RWRqiSp3wHsmdEgZlzoYdxI%2BIxVpHmsKn3O%2BKVA3mFIJ2m7YRECDGSM%2BW2IYTzo6FM4%2BdUIjURO8srzKSvJgZ%2BQ6R79arKQw3uHLlX\\\"}],\n\\\"group\\\":\\\"cf-nel\\\",\n\\\"max_age\\\":604800}\"],\n\"NEL\":[\"{\n\\\"success_fraction\\\":0.01,\n\\\"report_to\\\":\\\"cf-nel\\\",\n\\\"max_age\\\":604800}\"],\n\"Server\":[\"cloudflare\"]}} {\n\"correlation_id\":\"95236535-ec98-4541-b92a-adfa73b69eab\",\n\"trace_id\":\"c7ab8365-903f-46d4-9403-0e5b551e3545\"}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
5549352204577814028
|
6378757150172121188
|
idle
|
accessibility
|
NULL
|
Project: faVsco.js, menu
JY-20725-handle-HS-search Project: faVsco.js, menu
JY-20725-handle-HS-search-rate-limit, menu
Start Listening for PHP Debug Connections
HandleHubspotRateLimitTest
Run 'HandleHubspotRateLimitTest'
Debug 'HandleHubspotRateLimitTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
2
67
3
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Services\Crm\Hubspot;
use HubSpot\Client\Crm\Deals\ApiException as DealApiException;
use HubSpot\Client\Crm\Contacts\ApiException as ContactApiException;
use HubSpot\Client\Crm\Companies\ApiException as CompanyApiException;
use HubSpot\Client\Crm\Contacts\Model\SimplePublicObjectWithAssociations as ContactsWithAssociations;
use HubSpot\Client\Crm\Companies\Model\SimplePublicObjectWithAssociations as CompaniesWithAssociations;
use HubSpot\Client\Crm\Deals\Model\SimplePublicObjectWithAssociations as DealWithAssociations;
use HubSpot\Client\Crm\Objects\Model\SimplePublicObjectInput;
use HubSpot\Client\Crm\Objects\Model\SimplePublicObjectWithAssociations as ObjectWithAssociations;
use HubSpot\Client\Crm\Pipelines\Model\Error;
use HubSpot\Client\Crm\Pipelines\Model\PipelineStage;
use HubSpot\Client\Crm\Properties\Model\Property;
use HubSpot\Discovery\Discovery;
use Jiminny\Exceptions\CrmException;
use Jiminny\Exceptions\RateLimitException;
use Jiminny\Exceptions\SocialAccountTokenInvalidException;
use Jiminny\Jobs\Crm\NoteObject;
use Jiminny\Models\Crm\Field;
use Jiminny\Services\Crm\BaseClient;
use Jiminny\Services\Crm\Hubspot\DTO\Response\Owner;
use Jiminny\Services\SocialAccountService;
use SevenShores\Hubspot\Exceptions\BadRequest;
use SevenShores\Hubspot\Exceptions\HubspotException;
use SevenShores\Hubspot\Factory;
use SevenShores\Hubspot\Http\Response;
use Jiminny\Services\Crm\Hubspot\Pagination\HubspotPaginationService;
use Illuminate\Support\Facades\Redis;
use Throwable;
/**
* @phpstan-type CrmFieldOption array{id:string, label:string, value?:string}
*/
class Client extends BaseClient implements HubspotClientInterface
{
public const string MIN_API_VERSION = '2';
public const string BASE_URL = '[URL_WITH_CREDENTIALS] T
* @param callable(): T $apiCall
* @return T
*
* @throws RateLimitException
*/
private function executeRequest(callable $apiCall)
{
$cacheKey = $this->getRateLimitCacheKey();
$cachedRetryAfter = Redis::get($cacheKey);
if (is_string($cachedRetryAfter) && is_numeric($cachedRetryAfter)) {
throw new RateLimitException(
'Hubspot rate limit (cached circuit-breaker)',
(int) $cachedRetryAfter,
);
}
try {
return $apiCall();
} catch (Throwable $e) {
if ($this->isHubspotRateLimit($e)) {
$retryAfter = $this->parseRetryAfter($e);
Redis::setex($cacheKey, $retryAfter, (string) $retryAfter);
$this->log->warning('[Hubspot] Received 429 from API', [
'team_id' => $this->config->team_id,
'config_id' => $this->config->getId(),
'retry_after' => $retryAfter,
'message' => $this->parseMessage($e),
'reason' => $e->getMessage(),
]);
throw new RateLimitException('Hubspot returned 429', $retryAfter, $e);
}
throw $e;
}
}
private function getRateLimitCacheKey(): string
{
return sprintf('hubspot:ratelimit:portal:%d', $this->config->getId());
}
public function isHubspotRateLimit(Throwable $e): bool
{
if ($e instanceof BadRequest
|| $e instanceof DealApiException
|| $e instanceof ContactApiException
|| $e instanceof CompanyApiException
|| $e instanceof \GuzzleHttp\Exception\RequestException
) {
return (int) $e->getCode() === 429;
}
return false;
}
public function parseRetryAfter(Throwable $e): int
{
if (method_exists($e, 'getResponseHeaders')) {
$headers = $e->getResponseHeaders() ?: [];
$value = $headers['Retry-After'] ?? $headers['retry-after'] ?? null;
if (is_array($value)) {
$value = $value[0] ?? null;
}
if (is_numeric($value)) {
return (int) $value;
}
}
$message = strtolower($this->parseMessage($e));
if (str_contains($message, 'daily')) {
return 600;
}
if (str_contains($message, 'ten secondly')) {
return 10;
}
if (str_contains($message, 'secondly')) {
return 1;
}
$this->log->warning('[Hubspot] No retry-after header or known message, using default', [
'exception_class' => get_class($e),
'message' => $message,
]);
return 10;
}
public function parseMessage(Throwable $e): string
{
if (! method_exists($e, 'getResponseBody')) {
return '';
}
$body = $e->getResponseBody();
if (is_string($body)) {
$body = json_decode($body, true) ?? [];
}
if (! is_array($body)) {
return '';
}
return (string) ($body['message'] ?? '');
}
public function getMinimumApiVersion(): string
{
return self::MIN_API_VERSION;
}
public function getInstance(): Factory
{
return new Factory([
'key' => $this->accessToken,
'oauth2' => true,
'base_url' => $this->baseUrl,
]);
}
public function getNewInstance(): Discovery
{
return \HubSpot\Factory::createWithAccessToken($this->accessToken);
}
/**
* Secondly and daily limits for Hubspot API
*
* Product Tier: Free & Starter | Professional & Enterprise | API add-on (any tier)
* Burst: 100/10 seconds | 150/10 seconds | 200/10 seconds
* Daily: 250,000 | 500,000 | 1,000,000
*
* Official documentation states: The search endpoints are rate limited to five requests per second.
* Since with 5 RPS were still hitting secondly rate limits we lowered it to 4
*/
public function getPaginatedData(array $payload, string $type, int $offset = 0): array
{
$total = 0;
$lastId = null;
$rows = [];
foreach ($this->getPaginatedDataGenerator($payload, $type, $offset, $total, $lastId) as $row) {
$rows[] = $row;
}
return ['results' => $rows, 'total' => $total, 'last_record' => $lastId];
}
/**
* @throws HubspotException
* @throws SocialAccountTokenInvalidException
* @throws BadRequest
*/
public function getPaginatedDataGenerator(
array $payload,
string $type,
int $offset = 0,
int &$total = 0,
?string &$lastRecordId = null
): \Generator {
return $this->paginationService->getPaginatedDataGenerator(
$this,
$payload,
$type,
$offset,
$total,
$lastRecordId
);
}
/**
* Execute a search request against HubSpot CRM objects with rate limiting.
*
* @param string $objectType The object type ('deals', 'companies', 'contacts', 'calls')
* @param array<string, mixed> $payload The search payload with filters, sorts, properties, etc.
* @return array The search response with 'results', 'total', 'paging' keys
* @throws RateLimitException When rate limit is hit
* @throws HubspotException On API errors
*/
public function search(string $objectType, array $payload): array
{
$endpoint = self::BASE_URL . "/crm/v3/objects/{$objectType}/search";
return $this->executeRequest(function () use ($endpoint, $payload) {
$response = $this->getInstance()->getClient()->request('POST', $endpoint, ['json' => $payload]);
return $response->toArray();
});
}
/**
* @throws DealApiException
* @throws CrmException
*/
public function getOpportunityById(string $crmId, array $fields): array
{
try {
// $deal = $this->executeRequest(fn () => $this->getNewInstance()->crm()->deals()->basicApi()->getById(
$deal = $this->getNewInstance()->crm()->deals()->basicApi()->getById(
$crmId,
implode(',', $fields),
'companies,contacts'
);
} catch (DealApiException $e) {
$this->log->info('[Hubspot] Failed to fetch opportunity', [
'crm_id' => $crmId,
'reason' => $e->getMessage(),
]);
throw $e;
}
if (! $deal instanceof DealWithAssociations) {
throw new CrmException('Deal not found');
}
return [
'id' => $deal->getId(),
'properties' => $deal->getProperties(),
'associations' => $deal->getAssociations(),
];
}
/**
* Generic batch read method for HubSpot objects
*
* @param string $objectType The object type ('deals', 'companies', 'contacts')
* @param array<string> $crmIds Array of HubSpot object IDs (max 100)
* @param array<string> $fields Array of property names to fetch
*
* @return array<string, array> Array keyed by CRM ID with object data
*/
private function batchReadObjects(string $objectType, array $crmIds, array $fields): array
{
if (empty($crmIds)) {
return [];
}
$this->validateBatchSize($objectType, $crmIds);
$this->ensureValidToken();
try {
$batchConfig = $this->createBatchConfiguration($objectType);
$batchReadRequest = $this->prepareBatchRequest($batchConfig, $crmIds, $fields);
$response = $batchConfig['api']->read($batchReadRequest);
$this->validateApiResponse($response, $objectType);
$results = $this->processApiResults($response);
$this->logBatchResults($objectType, $crmIds, $results);
return $results;
} catch (\Throwable $e) {
$this->handleBatchError($e, $objectType, $crmIds);
}
}
private function validateBatchSize(string $objectType, array $crmIds): void
{
if (count($crmIds) > 100) {
throw new \InvalidArgumentException("Batch size cannot exceed 100 {$objectType}");
}
}
private function createBatchConfiguration(string $objectType): array
{
$configurations = [
'deals' => [
'batchReadRequest' => new \HubSpot\Client\Crm\Deals\Model\BatchReadInputSimplePublicObjectId(),
'inputClass' => \HubSpot\Client\Crm\Deals\Model\SimplePublicObjectId::class,
'api' => $this->getNewInstance()->crm()->deals()->batchApi(),
],
'companies' => [
'batchReadRequest' => new \HubSpot\Client\Crm\Companies\Model\BatchReadInputSimplePublicObjectId(),
'inputClass' => \HubSpot\Client\Crm\Companies\Model\SimplePublicObjectId::class,
'api' => $this->getNewInstance()->crm()->companies()->batchApi(),
],
'contacts' => [
'batchReadRequest' => new \HubSpot\Client\Crm\Contacts\Model\BatchReadInputSimplePublicObjectId(),
'inputClass' => \HubSpot\Client\Crm\Contacts\Model\SimplePublicObjectId::class,
'api' => $this->getNewInstance()->crm()->contacts()->batchApi(),
],
];
if (! isset($configurations[$objectType])) {
throw new \InvalidArgumentException("Unsupported object type: {$objectType}");
}
return $configurations[$objectType];
}
private function prepareBatchRequest(array $batchConfig, array $crmIds, array $fields): object
{
$batchReadRequest = $batchConfig['batchReadRequest'];
$inputClass = $batchConfig['inputClass'];
$inputs = array_map(function ($crmId) use ($inputClass) {
$input = new $inputClass();
$input->setId($crmId);
return $input;
}, $crmIds);
$batchReadRequest->setInputs($inputs);
$batchReadRequest->setProperties($fields);
return $batchReadRequest;
}
private function validateApiResponse($response, string $objectType): void
{
if (! $response) {
throw new CrmException("HubSpot API returned null response for {$objectType} batch read");
}
}
private function processApiResults($response): array
{
$results = [];
$responseResults = $response->getResults();
if ($responseResults) {
foreach ($responseResults as $object) {
if ($object && $object->getId()) {
$results[$object->getId()] = [
'id' => $object->getId(),
'properties' => $object->getProperties() ?: [],
];
}
}
}
return $results;
}
private function logBatchResults(string $objectType, array $crmIds, array $results): void
{
$this->log->info("[HubSpot] Batch fetched {$objectType}", [
'requested_count' => count($crmIds),
'returned_count' => count($results),
'crm_ids' => $crmIds,
]);
}
private function handleBatchError(\Throwable $e, string $objectType, array $crmIds): void
{
$errorMessage = $e->getMessage() ?: 'Unknown error';
$errorTrace = $e->getTraceAsString() ?: 'No trace available';
$this->log->error("[HubSpot] Failed to batch fetch {$objectType}", [
'crm_ids' => $crmIds,
'error' => $errorMessage,
'trace' => $errorTrace,
]);
throw new CrmException("Failed to batch fetch {$objectType}: " . $errorMessage);
}
/**
* Batch read multiple opportunities by their CRM IDs
*
* @param array<string> $crmIds Array of HubSpot deal IDs (max 100)
* @param array<string> $fields Array of property names to fetch
*
* @return array<string, array> Array keyed by CRM ID with opportunity data
*/
public function getOpportunitiesByIds(array $crmIds, array $fields): array
{
return $this->batchReadObjects('deals', $crmIds, $fields);
}
/**
* Batch read multiple companies by their CRM IDs
*
* @param array<string> $crmIds Array of HubSpot company IDs (max 100)
* @param array<string> $fields Array of property names to fetch
*
* @return array<string, array> Array keyed by CRM ID with company data
*/
public function getCompaniesByIds(array $crmIds, array $fields): array
{
return $this->batchReadObjects('companies', $crmIds, $fields);
}
/**
* Batch read multiple contacts by their CRM IDs
*
* @param array<string> $crmIds Array of HubSpot contact IDs (max 100)
* @param array<string> $fields Array of property names to fetch
*
* @return array<string, array> Array keyed by CRM ID with contact data
*/
public function getContactsByIds(array $crmIds, array $fields): array
{
return $this->batchReadObjects('contacts', $crmIds, $fields);
}
/**
* @throws CompanyApiException
* @throws CrmException
*/
public function getAccountById(string $crmId, array $fields): array
{
try {
$company = $this->getNewInstance()->crm()->companies()->basicApi()->getById(
$crmId,
implode(',', $fields),
);
} catch (CompanyApiException $e) {
$this->log->info('[Hubspot] Failed to fetch account', [
'crm_id' => $crmId,
'reason' => $e->getMessage(),
]);
throw $e;
}
if (! $company instanceof CompaniesWithAssociations) {
throw new CrmException('Account not found');
}
return [
'id' => $company->getId(),
'properties' => $company->getProperties(),
];
}
/**
* @throws ContactApiException
* @throws CrmException
*/
public function getContactById(string $crmId, array $fields): array
{
try {
$contact = $this->getNewInstance()->crm()->contacts()->basicApi()->getById(
$crmId,
implode(',', $fields)
);
} catch (ContactApiException $e) {
$this->log->info('[Hubspot] Failed to fetch contact', [
'crm_id' => $crmId,
'reason' => $e->getMessage(),
]);
throw $e;
}
if (! $contact instanceof ContactsWithAssociations) {
throw new CrmException('Contact not found');
}
return [
'id' => $contact->getId(),
'properties' => $contact->getProperties(),
];
}
/**
* This is email search request that Hubspot offers as GET (more generous quota)
*/
public function getContactByEmail(string $email, array $fields = []): array
{
try {
$contact = $this->getNewInstance()->crm()->contacts()->basicApi()->getById(
$email,
implode(',', $fields),
null,
false,
'email'
);
return [
'id' => $contact->getId(),
'properties' => $contact->getProperties(),
];
} catch (ContactApiException $e) {
$this->log->info('[Hubspot] Failed to fetch contact', [
'email' => $email,
'reason' => $e->getMessage(),
]);
return [];
}
}
/**
* @throws CrmException
*/
public function fetchProperty(string $objectType, string $propertyId): Property
{
$result = $this->getNewInstance()->crm()->properties()->coreApi()->getByName($objectType, $propertyId);
if (! $result instanceof Property) {
$this->log->error('[Hubspot] Failed to fetch property', [
'object_type' => $objectType,
'property_id' => $propertyId,
'reason' => $result->getMessage(),
]);
throw new CrmException('Failed to fetch property');
}
return $result;
}
/**
* @return array<CrmFieldOption>
*/
public function fetchPropertyOptions(string $objectType, string $propertyId): array
{
/** @var array<CrmFieldOption> */
return $this->fetchProperty($objectType, $propertyId)->getOptions();
}
/**
* @return array<array{id:string, label:string, deleted:bool}>
*/
public function fetchCallDispositions(): array
{
/** @var Response $response */
$response = $this->getInstance()->engagements()->getCallDispositions();
/**
* @var array<array{
* id:string,
* label:string,
* deleted: bool
* }>
*/
return $response->toArray();
}
/**
* @return array<CrmFieldOption>
*/
public function fetchOpportunityPipelineStages(): array
{
$stages = [];
$apiResponse = $this->getNewInstance()->crm()->pipelines()->pipelinesApi()->getAll('deals');
if ($apiResponse instanceof Error) {
$this->log->error('[Hubspot] Failed to fetch opportunity pipelines', [
'reason' => $apiResponse->getMessage(),
]);
return [];
}
foreach ($apiResponse->getResults() as $pipeline) {
$pipelineStages = array_map(
static function (PipelineStage $stage) {
return [
'id' => $stage->getId(),
'label' => $stage->getLabel(),
];
},
$pipeline->getStages()
);
$stages = array_merge($stages, $pipelineStages);
}
return $stages;
}
public function fetchOpportunityPipelines(): array
{
$pipelines = [];
try {
$apiResponse = $this->makeRequest('/crm/v3/pipelines/deals');
} catch (\Exception $e) {
$this->log->info('[Hubspot] Failed to fetch opportunity pipelines', [
'reason' => $e->getMessage(),
]);
return [];
}
$response = $apiResponse->toArray();
foreach ($response['results'] as $pipeline) {
$pipelines[] = [
'id' => $pipeline['id'],
'label' => $pipeline['label'],
];
}
return $pipelines;
}
/**
* @return array<CrmFieldOption>
*/
public function fetchMeetingOutcomeFieldOptions(Field $field): array
{
return $field->getCrmProviderId() === 'meetingOutcome'
? $this->fetchMeetingOutcomeTypes()
: $this->fetchCallActivityTypes();
}
public function fetchMeetingOutcomeTypes(): array
{
return $this->extractMeetingTypeOptions(
'[URL_WITH_CREDENTIALS] Response $response */
$response = $this->getInstance()
->getClient()
->request('GET', $endpoint);
/**
* @var array<array{
* value: string,
* label: string,
* displayOrder: int
* }> $optionData
*/
$optionData = $response->toArray()['options'] ?? [];
$options = [];
foreach ($optionData as $item) {
$options[] = [
'id' => $item['value'],
'value' => $item['value'],
'label' => $item['label'],
'display_order' => $item['displayOrder'],
];
}
return $options;
}
/**
* @return array<CrmFieldOption>
*/
public function fetchDispositionFieldOptions(): array
{
$options = [];
$dispositions = $this->fetchCallDispositions();
foreach ($dispositions as $disposition) {
if ($disposition['deleted'] !== false) {
continue;
}
$option['value'] = $disposition['id'];
$option['id'] = $disposition['id'];
$option['label'] = $disposition['label'];
$options[] = $option;
}
return $options;
}
/**
* @return array<CrmFieldOption>
*/
public function fetchOpportunityFieldOptions(Field $field): array
{
if ($field->isStageField()) {
return $this->fetchOpportunityPipelineStages();
}
if ($field->isPipelineField()) {
return $this->fetchOpportunityPipelines();
}
return $this->fetchPropertyOptions('deals', $field->getCrmProviderId());
}
/**
* @throws BadRequest
* @throws HubspotException
*/
public function makeRequest(string $endpoint, $method = 'GET', $payload = [], ?string $queryString = null)
{
$endpoint = self::BASE_URL . $endpoint;
if ($method === 'GET') {
return $this->getInstance()->getClient()?->request(
method: $method,
endpoint: $endpoint,
query_string: $queryString
);
} else {
return $this->getInstance()->getClient()->request($method, $endpoint, [
'json' => ($payload),
]);
}
}
/**
* @throws BadRequest
* @throws HubspotException
*/
public function createMeeting(array $payload): Response
{
$endpoint = '/crm/v3/objects/meetings';
return $this->makeRequest($endpoint, 'POST', $payload);
}
/**
* @throws BadRequest
* @throws HubspotException
*/
public function updateMeeting(string $meetingId, array $payload): Response
{
$endpoint = '/crm/v3/objects/meetings/' . $meetingId;
return $this->makeRequest($endpoint, 'PATCH', $payload);
}
/**
* @throws \Exception
*/
public function createNote(
string $body,
string $ownerId,
int $timestamp,
string $objectId,
NoteObject $noteObject
): ?string {
try {
$noteInput = new SimplePublicObjectInput([
'properties' => [
'hs_note_body' => $body,
'hubspot_owner_id' => $ownerId,
'hs_timestamp' => $timestamp,
],
]);
// Create note
$note = $this->getNewInstance()->crm()->objects()->basicApi()->create('note', $noteInput);
$this->getNewInstance()->crm()->objects()->associationsApi()->create(
'note',
$note->getId(),
$this->getNoteObject($noteObject),
$objectId,
$this->getNoteAssociationType($noteObject),
);
return $note->getId();
} catch (\Exception $e) {
$this->log->error('[Hubspot] Failed to create note', [
'objectId' => $objectId,
'noteObject' => $noteObject->getObjectType(),
'reason' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
return null;
}
public function updateEngagement(string $objectId, array $engagement, array $metadata): void
{
$this->getInstance()->engagements()->update($objectId, $engagement, $metadata);
}
public function getEngagementData(string $engagementId): array
{
$engagement = $this->getInstance()->engagements()->get($engagementId);
return $engagement->toArray();
}
public function createEngagement(array $engagement, array $associations, array $metadata): Response
{
return $this->getInstance()
->engagements()
->create($engagement, $associations, $metadata);
}
public function isUnauthorizedException(\Exception $e): bool
{
// Check for specific HubSpot API exception types first
if ($e instanceof BadRequest) {
// BadRequest can contain 401 status codes
return $e->getCode() === 401;
}
// Check for HTTP client exceptions with status codes
if ($e instanceof \GuzzleHttp\Exception\RequestException && $e->hasResponse()) {
$response = $e->getResponse();
if ($response !== null) {
return $response->getStatusCode() === 401;
}
}
// Check for Guzzle HTTP exceptions
if ($e instanceof \GuzzleHttp\Exception\ClientException) {
return $e->getCode() === 401;
}
// Fallback to string matching as last resort, but be more specific
$message = strtolower($e->getMessage());
return str_contains($message, '401 unauthorized') ||
str_contains($message, 'http 401') ||
str_contains($message, 'status code 401') ||
(preg_match('/\b401\b/', $message) && str_contains($message, 'unauthorized'));
}
/**
* Validates and refreshes the access token if needed before API requests.
* This ensures long-running processes don't fail due to token expiration.
*
* @throws SocialAccountTokenInvalidException
*/
public function ensureValidToken(): void
{
if ($this->oauthAccount === null) {
return;
}
$newToken = $this->tokenManager->ensureValidToken($this->oauthAccount);
if ($newToken !== null) {
$this->accessToken = $newToken;
}
}
public function getConfig()
{
return $this->config;
}
// returns only active (archived=false)
public function getOwners(): array
{
return $this->getNewInstance()->crm()->owners()->getAll();
}
/**
* @param bool $archived
*
* @return array<Owner>|[]
*/
public function getOwnersArchived(bool $archived = true): array
{
$endpoint = '/crm/v3/owners';
$queryParams = [
'archived' => $archived ? 'true' : 'false',
];
$queryString = http_build_query($queryParams);
$owners = [];
try {
$response = $this->makeRequest(endpoint: $endpoint, queryString: $queryString);
$responseData = $response?->toArray();
foreach ($responseData['results'] as $result) {
try {
$owners[] = Owner::create($result);
} catch (Throwable $e) {
$this->log->error('[HubSpot] Failed to process owner data', [
'result' => $result,
'error' => $e->getMessage(),
]);
continue;
}
}
} catch (Throwable $e) {
$this->log->error('HubSpot] Failed to fetch owners', [
'archived' => $archived,
'error' => $e->getMessage(),
]);
return [];
}
return $owners;
}
public function getMeeting(string $engagementId): ObjectWithAssociations
{
return $this->getNewInstance()->crm()->objects()->basicApi()
->getById('meeting', $engagementId, null, 'contact,company,deal');
}
public function deleteEngagement(string $engagementId): void
{
$this->getInstance()->engagements()->delete((int) $engagementId);
}
public function getAssociationsData(array $ids, string $fromObject, string $toObject): array
{
$associationData = [];
$idChunks = array_chunk($ids, self::ASSOCIATIONS_BATCH_SIZE_LIMIT);
foreach ($idChunks as $idChunk) {
try {
$batchInput = new \HubSpot\Client\Crm\Associations\Model\BatchInputPublicObjectId();
$batchInput->setInputs(array_map(function ($id) {
$publicObjectId = new \HubSpot\Client\Crm\Associations\Model\PublicObjectId();
$publicObjectId->setId($id);
return $publicObjectId;
}, $idChunk));
$associatedObjectsData = $this
->getNewInstance()
->crm()
->associations()
->batchApi()
->read($fromObject, $toObject, $batchInput);
if ($associatedObjectsData instanceof \HubSpot\Client\Crm\Associations\Model\BatchResponsePublicAssociationMulti) {
foreach ($associatedObjectsData->getResults() as $association) {
$from = $association->getFrom()->getId();
$toAssociations = $association->getTo();
if (! empty($toAssociations)) {
$associationData[$from] = array_map(function ($item) {
return $item->getId();
}, $toAssociations);
}
}
}
// } catch (RateLimitException $e) {
// throw $e;
} catch (\Exception $e) {
$this->log->error('[Hubspot] Failed to fetch associations', [
'from_object' => $fromObject,
'to_object' => $toObject,
'reason' => $e->getMessage(),
]);
}
}
return $associationData;
}
/**
* @throws \Exception
*/
private function getNoteAssociationType(NoteObject $noteObject): string
{
return match($noteObject) {
NoteObject::Opportunity => 'note_to_deal',
NoteObject::Lead, NoteObject::Contact => 'note_to_contact', // or 'note_to_lead' if your portal supports it
NoteObject::Account => 'note_to_company',
NoteObject::Call, NoteObject::Event => throw new \Exception('Not supported'),
};
}
/**
* @throws \Exception
*/
private function getNoteObject(NoteObject $noteObject): string
{
return match($noteObject) {
NoteObject::Opportunity => 'deal',
NoteObject::Lead, NoteObject::Contact => 'contact',
NoteObject::Account => 'company',
NoteObject::Call, NoteObject::Event => throw new \Exception('Not supported'),
};
}
public function addAssociations(string $objectType, string $associationType, array $payload): Response
{
$endpoint = "/crm/v4/associations/$objectType/$associationType/batch/create";
return $this->makeRequest($endpoint, 'POST', $payload);
}
public function removeAssociations(string $objectType, string $associationType, array $payload): Response
{
$endpoint = "/crm/v4/associations/$objectType/$associationType/batch/archive";
return $this->makeRequest($endpoint, 'POST', $payload);
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
19
Previous Highlighted Error
Next Highlighted Error
[2026-05-07 14:21:15] local.INFO: [Hubspot] DEBUG Getting headers {
"headers":{
"Date":["Thu,07 May 2026 14:21:15 GMT"],
"Content-Type":["application/json;charset=utf-8"],
"Transfer-Encoding":["chunked"],
"Connection":["keep-alive"],
"CF-Ray":["9f80deb8db60dc3a-SOF"],
"CF-Cache-Status":["DYNAMIC"],
"Strict-Transport-Security":["max-age=31536000; includeSubDomains; preload"],
"Vary":["origin,
accept-encoding"],
"access-control-allow-credentials":["false"],
"server-timing":["hcid;desc=\"019e02d0-6fd8-7812-bdba-885b7ccb3ee3\",
cfr;desc=\"9f80deb8e7c6dc3a-IAD\""],
"x-content-type-options":["nosniff"],
"x-hubspot-correlation-id":["019e02d0-6fd8-7812-bdba-885b7ccb3ee3"],
"Set-Cookie":["__cf_bm=SIUrtdQgXVrik50pdqF6hZVYKhzTnQBidvMabeCtm0Y-1778163675-[IP_ADDRESS]-rI.ZggtDKxTge5zr8_2gbBfWMQQ.ufZEXDZyHz2mBUFdzdo2gTHEsOkXMSEShjK0hGYxNhUGM1ZoBpX7BcFZcHEjA7Cs_.SMUhUnd2nYjko; path=/; expires=Thu,
07-May-26 14:51:15 GMT; domain=.hubapi.com; HttpOnly; Secure; SameSite=None"],
"Report-To":["{
\"endpoints\":[{
\"url\":\"https:\\/\\/a.nel.cloudflare.com\\/report\\/v4?s=NYAlsVTP0fYm32qrSDjxYE4sd2RWRqiSp3wHsmdEgZlzoYdxI%2BIxVpHmsKn3O%2BKVA3mFIJ2m7YRECDGSM%2BW2IYTzo6FM4%2BdUIjURO8srzKSvJgZ%2BQ6R79arKQw3uHLlX\"}],
\"group\":\"cf-nel\",
\"max_age\":604800}"],
"NEL":["{
\"success_fraction\":0.01,
\"report_to\":\"cf-nel\",
\"max_age\":604800}"],
"Server":["cloudflare"]}} {
"correlation_id":"95236535-ec98-4541-b92a-adfa73b69eab",
"trace_id":"c7ab8365-903f-46d4-9403-0e5b551e3545"}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
18186
|
NULL
|
NULL
|
NULL
|
|
18189
|
791
|
0
|
2026-05-11T11:03:34.637064+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-11/1778 /Users/lukas/.screenpipe/data/data/2026-05-11/1778497414637_m2.jpg...
|
PhpStorm
|
faVsco.js – Client.php
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
JY-20725-handle-HS-search Project: faVsco.js, menu
JY-20725-handle-HS-search-rate-limit, menu
Start Listening for PHP Debug Connections
HandleHubspotRateLimitTest
Run 'HandleHubspotRateLimitTest'
Debug 'HandleHubspotRateLimitTest'
More Actions
JetBrains AI...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"bounds":{"left":0.025930852,"top":0.019952115,"width":0.03856383,"height":0.025538707},"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JY-20725-handle-HS-search-rate-limit, menu","depth":5,"bounds":{"left":0.064494684,"top":0.019952115,"width":0.09541223,"height":0.025538707},"on_screen":true,"help_text":"Git Branch: JY-20725-handle-HS-search-rate-limit","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"bounds":{"left":0.82413566,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"HandleHubspotRateLimitTest","depth":6,"bounds":{"left":0.8394282,"top":0.019952115,"width":0.076130316,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'HandleHubspotRateLimitTest'","depth":6,"bounds":{"left":0.9155585,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'HandleHubspotRateLimitTest'","depth":6,"bounds":{"left":0.9268617,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"bounds":{"left":0.9381649,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"bounds":{"left":0.96609044,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
5685266402682420683
|
-8926130836387591936
|
idle
|
hybrid
|
NULL
|
Project: faVsco.js, menu
JY-20725-handle-HS-search Project: faVsco.js, menu
JY-20725-handle-HS-search-rate-limit, menu
Start Listening for PHP Debug Connections
HandleHubspotRateLimitTest
Run 'HandleHubspotRateLimitTest'
Debug 'HandleHubspotRateLimitTest'
More Actions
JetBrains AI
PhostormINavicarecodeFV faVsco.js°9 JY-20725-handle-HS-search-rate-linProiect© BatchSyncCollectol•JiminnyDeougcommane.onge balchsynckealsseo closedDealstagesso RematchActivityonCrmObiectDetach.pngDealrielasservice.gc)Decorateacuivilv.or© FieldDefinitions.phrC) FieldT vpeconvertee Hubspotclientinterc) Hubspotlokenman© PayloadBuilder.phpC) RemotecrmobiectrP ResponseNormalizec) Service.onrC)SvncFieldAction.onC) SvncRelatedActivitC) WebhookSvncBatclv MintearationAorM Acceccors• D ConfigDDTO• M SiltersD Jobs• M ProcnectSearchStr.W sevice lraits© DataClient.php© DecorateActivity.phc Localsearch.oneu Localsearchinteria© RemoteSearch.phpc) Service.phpv W Listeners© ConvertLeadActivitc) PurceLookuocache> M Metadata> MiarationM Pipedrivev Salesforce• D FieldsM OnnortunitvMatcheMOnnortunitvSvneSt,M ProsneetSearchStr.M ServiceTraitcC) Client nhr© DecorateActivity.ph. Delete@biectsTrait© FieldDefinitions.php© PayloadBuilder.php© Profile.php© QueryBuilder.phpTacts nacced. 12 (1 hour adoiRateLimitexcedC) CrmObiectsResolver.pho() ProviderRateLimiter.phpclass Cuient extends BasecLient imolements Hubspotcuientinterfacepublic function getPaginatedDataGenerator(neturnSthis->naginationService->aetPaginatedDataGenenatordSthis$payload,$type,sotrset,a scocal,2. Clac+RocondTdi243244 FExecute a search request against Hubspot ukn obnects with rate Limitinq.247•doaram strina sobzectlupe lne obzect tupe deals''contacts', 'calls')* oparam arrou‹strino. maxed› sooulodd lne search poulond with tzlters. sorts. provertzes. etc* @return array The search response with 'results', 'total', 'paging' keys* othrows RatelimitExcention When rate limit is hit* othrows HubspotExceotion On APi errors253 CTpubuic function searchistring Sobiectivoe. array Soavload): arravSendpoint = self::BASE_URL • "/crm/v3/objects/{SobjectTypes/search";return $this->executeRequest(function ( use (Sendpoint, $payload) {Sresnonse = Sthis->aetInstance@->aetCZient@->request/method)'POST', Sendpoint, ['json' => $payload]):neturn Sresnonse->toArravo264* Othrows DealApiException266* athrows CrmExceptionpublic function getOpportunityById(string ScrmId, array $fields): array//1Sdeal = Sthis-›executeRequest(fn () => Sthis->qetNewInstance()->crm()->deals(->basicApi@->getBvId(Sdeal = Sthis->qetNewInstance@->crm@->deals@->basicApi@->qetBvId(Scrmiidlimolode separator: ".'. Sfields)42 A67 X3AII II IIII | M01111I4 HS_local (jiminny@localhost]# console [PKoD)A console (EU]A console [STAGING][2026-05-07 14:21:15] local.INF0: [Hubspot] DEBUG Getting headers {w19A"Vace".L"Inu,or May 2020 14.21.15 bMlJ"Connection":"keep-alive"]r"CF-Ray":"9t80deb8dbo0dcsa-S0F")r"Strict-Transport-Security":"max-aqe=31536000* 1ncLudesubDomains: preload")."server-timing": ["hcid;desc=|"019e02d0-6fd8-7812-bdba-885b7ccb3ee3)","x-hubspot-correlation-id":["019e02d0-6fd8-7812-bdba-885b7ccb3ee3"],"Set-Cookie":["__cf_bm=S1UrtdQgXVrik50pdqF6hZVYKhzTnQBidvMabeCtm0Y-1778163675-[IP_ADDRESS]-rI.ZggtDKxTg07-May-26 14:51:15 GMT; domain=.hubapi.com; Http0nly; Secure; SameSite=None"],\"url\":"https:|\/\\/a.nel.cloudflare.com\\/report\\/v4?s=NYAlsVTP0fYm32qrSDjxYE4sd2RWRqiSp3wHsmdEg2\"group\":\"cf-nel\",\"max_age)":604800}"],"NEL".L"1\"success_fraction\":0.01,("max age ":604800}"]"Server": ["cloudflare"]}} {"correlation id":"95236535-ec98-4541-b92a-adfa73b69eab"."trace_1d":"C/Ab8565-905t-4604-9405-0e5b551e5545'5helPreparation tor kell... In o/ m100% Lz• Mon 11 May 14:03:34HandleHubspotRateLimitTest vCascade• HubSpot Rate Limit+0 ..lets simplify it, just call it parseMessage and skip policy al togetherThought for 5s ›Read Client.php #L86-200b Client.php+17 -35Verity nothing broke:Command docker. tailtests/Unit/Services/Crm/Hubspot/ClientTest.php --no-coverage 2>&1 | tail -10attaesmopup92.20contiourat ion:nome1iminnyonounit.xml•[PASSWORD_DOTS]DD..DD..D.D..DDDDD.DDD...D[PASSWORD_DOTS] 65 /80 (1818)Time: 02:14.415. Memorv: 62.00 MBOk but there were iccuec!Ask anvthina (84L)Claude Onus 1.7 MediumWN Windsurf Toams 250-1UTF.8.fo 4 spaces...
|
18187
|
NULL
|
NULL
|
NULL
|
|
18190
|
790
|
1
|
2026-05-11T11:03:35.290679+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-11/1778 /Users/lukas/.screenpipe/data/data/2026-05-11/1778497415290_m1.jpg...
|
PhpStorm
|
faVsco.js – Client.php
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
JY-20725-handle-HS-search Project: faVsco.js, menu
JY-20725-handle-HS-search-rate-limit, menu
Start Listening for PHP Debug Connections
HandleHubspotRateLimitTest
Run 'HandleHubspotRateLimitTest'
Debug 'HandleHubspotRateLimitTest'
More Actions...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JY-20725-handle-HS-search-rate-limit, menu","depth":5,"on_screen":true,"help_text":"Git Branch: JY-20725-handle-HS-search-rate-limit","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"HandleHubspotRateLimitTest","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'HandleHubspotRateLimitTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'HandleHubspotRateLimitTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
3299086470291663550
|
-8349389703787247352
|
click
|
hybrid
|
NULL
|
Project: faVsco.js, menu
JY-20725-handle-HS-search Project: faVsco.js, menu
JY-20725-handle-HS-search-rate-limit, menu
Start Listening for PHP Debug Connections
HandleHubspotRateLimitTest
Run 'HandleHubspotRateLimitTest'
Debug 'HandleHubspotRateLimitTest'
More Actions
SlackFileEditViewGoHistoryWindowHelpDOCKER181DEV (docker)$2APP (-zsh)883viewsjiminny-worker-processing-2:jiminny-worker-processing-2_00: stoppedjiminny-worker-processing-3:jiminny-worker-processing-3_00: stoppedjiminny-worker-processing-4: jiminny-worker-processing-4_00: stoppedJiminny-worker-processing-5:j1minny-worker-processing-5_00:jiminny-worker-processing-delayed: jiminny-worker-processing-delayed_00: stoppedworker-analytics:worker-analytics_00: stoppedworker-conferences:worker-conferences_00: stoppedworker-crm-update:worker-crm-update_00: stoppedworker-download:worker-download_00: stoppedworker-emails:worker-emails_00: stoppedworker-nudges:worker-nudges_00: stoppedworker:worker_00: stoppedworker-audio:worker-audio_00: stoppedworker-calendar:worker-calendar_00: stoppedworker-crm-sync:worker-crm-sync_00: stoppedartisan-schedule:artisan-schedule_00: stoppedworker-es-update:worker-es-update_00: stoppedjiminny-worker-processing-1:jiminny-worker-processing-1_00: stoppedartisan-schedule:artisan-schedule_00: startedjiminny-worker-processing-1:jiminny-worker-processing-1_00: startedjiminny-worker-processing-2:jiminny-worker-processing-2_00: startedjiminny-worker-processing-3:jiminny-worker-processing-3_00: startedjiminny-worker-processing-4:jiminny-worker-processing-4_00: startedjiminny-worker-processing-5:jiminny-worker-processing-5_00:jiminny-worker-processing-delayed: jiminny-worker-processing-delayed_00: startedworker:worker_00: startedworker-analytics:worker-analytics_00: startedworker-audio:worker-audio_00: startedworker-calendar:worker-calendar_00: startedworker-conferences:worker-conferences_00: startedworker-crm-sync:worker-crm-sync_00: startedworker-crm-update:worker-crm-update_00: startedworker-download:worker-download_00: startedworker-emails:worker-emails_00: startedworker-es-update:worker-es-update_00: startedworker-nudges:worker-nudges_00: startedroot@docker_lamp_1:/home/jiminny#php artisan jiminny:debugDispatching 100 MatchActivityCrmData jobs (portal=2)Done. Watch logs and run jiminny:debug observeRateLimit to inspect cache state.root@docker_lamp_1:/home/jiminny# php artisan jiminny:debugDispatching 100 MatchActivityCrmData jobs (portal=2)Done. Watch logs and run jiminny:debug observeRateLimit to inspect cache state.root@docker_lamp_1:/home/jiminny# ]DEV (d)-zsh• .HomeDMsActivityLater...liholg Preparation for Refi... in 57 m100% <78• Mon 11 May14:03:34→Describe what you are looking forJiminny ...# contusion-clinic# curiosity_lab# engineering# general# jiminny-bg# platform-tickets# product_launches# random# releases# sofia-office# support# thank-yous# the_people_of jimi...ToastHomeMessages^ Direct messages®. Aneliya Angelova®. Galya Dimitrova I& Petko Kashinski&. Stefka StoyanovaVasil Vasilev&. Nikolay IvanovAneliya Angelova, ...Stoyan Tanev®. VesE Lukas Kovalik y... 0AboutiminnyFriday, May 8th ~Added by Toast for GitHubToday ~Toast APP10:00 AMReviewapp#12059 Jy 20820 es reindexstream model hydration2 days old • 12 files changed •@Vasil VasilevAdded by Toast for GitHubResolve Conflictsapp#11443 Test hublets latency5 months old • 20 files changed#11327 JY-19501 webhookbased opportunity syncShow moreAdded by Toast for GitHubNeeds Loveapp#12024 JY-20773 fix user pilottracking ofr automated reportgenerated12 days old • 1 file changedE:: AppsMessage ToastJira CloudToast+AaConala CalaNew...
|
18186
|
NULL
|
NULL
|
NULL
|
|
18191
|
790
|
2
|
2026-05-11T11:03:39.911088+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-11/1778 /Users/lukas/.screenpipe/data/data/2026-05-11/1778497419911_m1.jpg...
|
PhpStorm
|
faVsco.js – Client.php
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
JY-20725-handle-HS-search Project: faVsco.js, menu
JY-20725-handle-HS-search-rate-limit, menu
Start Listening for PHP Debug Connections
HandleHubspotRateLimitTest
Run 'HandleHubspotRateLimitTest'
Debug 'HandleHubspotRateLimitTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
2
67
3
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Services\Crm\Hubspot;
use HubSpot\Client\Crm\Deals\ApiException as DealApiException;
use HubSpot\Client\Crm\Contacts\ApiException as ContactApiException;
use HubSpot\Client\Crm\Companies\ApiException as CompanyApiException;
use HubSpot\Client\Crm\Contacts\Model\SimplePublicObjectWithAssociations as ContactsWithAssociations;
use HubSpot\Client\Crm\Companies\Model\SimplePublicObjectWithAssociations as CompaniesWithAssociations;
use HubSpot\Client\Crm\Deals\Model\SimplePublicObjectWithAssociations as DealWithAssociations;
use HubSpot\Client\Crm\Objects\Model\SimplePublicObjectInput;
use HubSpot\Client\Crm\Objects\Model\SimplePublicObjectWithAssociations as ObjectWithAssociations;
use HubSpot\Client\Crm\Pipelines\Model\Error;
use HubSpot\Client\Crm\Pipelines\Model\PipelineStage;
use HubSpot\Client\Crm\Properties\Model\Property;
use HubSpot\Discovery\Discovery;
use Jiminny\Exceptions\CrmException;
use Jiminny\Exceptions\RateLimitException;
use Jiminny\Exceptions\SocialAccountTokenInvalidException;
use Jiminny\Jobs\Crm\NoteObject;
use Jiminny\Models\Crm\Field;
use Jiminny\Services\Crm\BaseClient;
use Jiminny\Services\Crm\Hubspot\DTO\Response\Owner;
use Jiminny\Services\SocialAccountService;
use SevenShores\Hubspot\Exceptions\BadRequest;
use SevenShores\Hubspot\Exceptions\HubspotException;
use SevenShores\Hubspot\Factory;
use SevenShores\Hubspot\Http\Response;
use Jiminny\Services\Crm\Hubspot\Pagination\HubspotPaginationService;
use Illuminate\Support\Facades\Redis;
use Throwable;
/**
* @phpstan-type CrmFieldOption array{id:string, label:string, value?:string}
*/
class Client extends BaseClient implements HubspotClientInterface
{
public const string MIN_API_VERSION = '2';
public const string BASE_URL = '[URL_WITH_CREDENTIALS] T
* @param callable(): T $apiCall
* @return T
*
* @throws RateLimitException
*/
private function executeRequest(callable $apiCall)
{
$cacheKey = $this->getRateLimitCacheKey();
$cachedRetryAfter = Redis::get($cacheKey);
if (is_string($cachedRetryAfter) && is_numeric($cachedRetryAfter)) {
throw new RateLimitException(
'Hubspot rate limit (cached circuit-breaker)',
(int) $cachedRetryAfter,
);
}
try {
return $apiCall();
} catch (Throwable $e) {
if ($this->isHubspotRateLimit($e)) {
$retryAfter = $this->parseRetryAfter($e);
Redis::setex($cacheKey, $retryAfter, (string) $retryAfter);
$this->log->warning('[Hubspot] Received 429 from API', [
'team_id' => $this->config->team_id,
'config_id' => $this->config->getId(),
'retry_after' => $retryAfter,
'message' => $this->parseMessage($e),
'reason' => $e->getMessage(),
]);
throw new RateLimitException('Hubspot returned 429', $retryAfter, $e);
}
throw $e;
}
}
private function getRateLimitCacheKey(): string
{
return sprintf('hubspot:ratelimit:portal:%d', $this->config->getId());
}
public function isHubspotRateLimit(Throwable $e): bool
{
if ($e instanceof BadRequest
|| $e instanceof DealApiException
|| $e instanceof ContactApiException
|| $e instanceof CompanyApiException
|| $e instanceof \GuzzleHttp\Exception\RequestException
) {
return (int) $e->getCode() === 429;
}
return false;
}
public function parseRetryAfter(Throwable $e): int
{
if (method_exists($e, 'getResponseHeaders')) {
$headers = $e->getResponseHeaders() ?: [];
$value = $headers['Retry-After'] ?? $headers['retry-after'] ?? null;
if (is_array($value)) {
$value = $value[0] ?? null;
}
if (is_numeric($value)) {
return (int) $value;
}
}
$message = strtolower($this->parseMessage($e));
if (str_contains($message, 'daily')) {
return 600;
}
if (str_contains($message, 'ten secondly')) {
return 10;
}
if (str_contains($message, 'secondly')) {
return 1;
}
$this->log->warning('[Hubspot] No retry-after header or known message, using default', [
'exception_class' => get_class($e),
'message' => $message,
]);
return 10;
}
public function parseMessage(Throwable $e): string
{
if (! method_exists($e, 'getResponseBody')) {
return '';
}
$body = $e->getResponseBody();
if (is_string($body)) {
$body = json_decode($body, true) ?? [];
}
if (! is_array($body)) {
return '';
}
return (string) ($body['message'] ?? '');
}
public function getMinimumApiVersion(): string
{
return self::MIN_API_VERSION;
}
public function getInstance(): Factory
{
return new Factory([
'key' => $this->accessToken,
'oauth2' => true,
'base_url' => $this->baseUrl,
]);
}
public function getNewInstance(): Discovery
{
return \HubSpot\Factory::createWithAccessToken($this->accessToken);
}
/**
* Secondly and daily limits for Hubspot API
*
* Product Tier: Free & Starter | Professional & Enterprise | API add-on (any tier)
* Burst: 100/10 seconds | 150/10 seconds | 200/10 seconds
* Daily: 250,000 | 500,000 | 1,000,000
*
* Official documentation states: The search endpoints are rate limited to five requests per second.
* Since with 5 RPS were still hitting secondly rate limits we lowered it to 4
*/
public function getPaginatedData(array $payload, string $type, int $offset = 0): array
{
$total = 0;
$lastId = null;
$rows = [];
foreach ($this->getPaginatedDataGenerator($payload, $type, $offset, $total, $lastId) as $row) {
$rows[] = $row;
}
return ['results' => $rows, 'total' => $total, 'last_record' => $lastId];
}
/**
* @throws HubspotException
* @throws SocialAccountTokenInvalidException
* @throws BadRequest
*/
public function getPaginatedDataGenerator(
array $payload,
string $type,
int $offset = 0,
int &$total = 0,
?string &$lastRecordId = null
): \Generator {
return $this->paginationService->getPaginatedDataGenerator(
$this,
$payload,
$type,
$offset,
$total,
$lastRecordId
);
}
/**
* Execute a search request against HubSpot CRM objects with rate limiting.
*
* @param string $objectType The object type ('deals', 'companies', 'contacts', 'calls')
* @param array<string, mixed> $payload The search payload with filters, sorts, properties, etc.
* @return array The search response with 'results', 'total', 'paging' keys
* @throws RateLimitException When rate limit is hit
* @throws HubspotException On API errors
*/
public function search(string $objectType, array $payload): array
{
$endpoint = self::BASE_URL . "/crm/v3/objects/{$objectType}/search";
return $this->executeRequest(function () use ($endpoint, $payload) {
$response = $this->getInstance()->getClient()->request('POST', $endpoint, ['json' => $payload]);
return $response->toArray();
});
}
/**
* @throws DealApiException
* @throws CrmException
*/
public function getOpportunityById(string $crmId, array $fields): array
{
try {
// $deal = $this->executeRequest(fn () => $this->getNewInstance()->crm()->deals()->basicApi()->getById(
$deal = $this->getNewInstance()->crm()->deals()->basicApi()->getById(
$crmId,
implode(',', $fields),
'companies,contacts'
);
} catch (DealApiException $e) {
$this->log->info('[Hubspot] Failed to fetch opportunity', [
'crm_id' => $crmId,
'reason' => $e->getMessage(),
]);
throw $e;
}
if (! $deal instanceof DealWithAssociations) {
throw new CrmException('Deal not found');
}
return [
'id' => $deal->getId(),
'properties' => $deal->getProperties(),
'associations' => $deal->getAssociations(),
];
}
/**
* Generic batch read method for HubSpot objects
*
* @param string $objectType The object type ('deals', 'companies', 'contacts')
* @param array<string> $crmIds Array of HubSpot object IDs (max 100)
* @param array<string> $fields Array of property names to fetch
*
* @return array<string, array> Array keyed by CRM ID with object data
*/
private function batchReadObjects(string $objectType, array $crmIds, array $fields): array
{
if (empty($crmIds)) {
return [];
}
$this->validateBatchSize($objectType, $crmIds);
$this->ensureValidToken();
try {
$batchConfig = $this->createBatchConfiguration($objectType);
$batchReadRequest = $this->prepareBatchRequest($batchConfig, $crmIds, $fields);
$response = $batchConfig['api']->read($batchReadRequest);
$this->validateApiResponse($response, $objectType);
$results = $this->processApiResults($response);
$this->logBatchResults($objectType, $crmIds, $results);
return $results;
} catch (\Throwable $e) {
$this->handleBatchError($e, $objectType, $crmIds);
}
}
private function validateBatchSize(string $objectType, array $crmIds): void
{
if (count($crmIds) > 100) {
throw new \InvalidArgumentException("Batch size cannot exceed 100 {$objectType}");
}
}
private function createBatchConfiguration(string $objectType): array
{
$configurations = [
'deals' => [
'batchReadRequest' => new \HubSpot\Client\Crm\Deals\Model\BatchReadInputSimplePublicObjectId(),
'inputClass' => \HubSpot\Client\Crm\Deals\Model\SimplePublicObjectId::class,
'api' => $this->getNewInstance()->crm()->deals()->batchApi(),
],
'companies' => [
'batchReadRequest' => new \HubSpot\Client\Crm\Companies\Model\BatchReadInputSimplePublicObjectId(),
'inputClass' => \HubSpot\Client\Crm\Companies\Model\SimplePublicObjectId::class,
'api' => $this->getNewInstance()->crm()->companies()->batchApi(),
],
'contacts' => [
'batchReadRequest' => new \HubSpot\Client\Crm\Contacts\Model\BatchReadInputSimplePublicObjectId(),
'inputClass' => \HubSpot\Client\Crm\Contacts\Model\SimplePublicObjectId::class,
'api' => $this->getNewInstance()->crm()->contacts()->batchApi(),
],
];
if (! isset($configurations[$objectType])) {
throw new \InvalidArgumentException("Unsupported object type: {$objectType}");
}
return $configurations[$objectType];
}
private function prepareBatchRequest(array $batchConfig, array $crmIds, array $fields): object
{
$batchReadRequest = $batchConfig['batchReadRequest'];
$inputClass = $batchConfig['inputClass'];
$inputs = array_map(function ($crmId) use ($inputClass) {
$input = new $inputClass();
$input->setId($crmId);
return $input;
}, $crmIds);
$batchReadRequest->setInputs($inputs);
$batchReadRequest->setProperties($fields);
return $batchReadRequest;
}
private function validateApiResponse($response, string $objectType): void
{
if (! $response) {
throw new CrmException("HubSpot API returned null response for {$objectType} batch read");
}
}
private function processApiResults($response): array
{
$results = [];
$responseResults = $response->getResults();
if ($responseResults) {
foreach ($responseResults as $object) {
if ($object && $object->getId()) {
$results[$object->getId()] = [
'id' => $object->getId(),
'properties' => $object->getProperties() ?: [],
];
}
}
}
return $results;
}
private function logBatchResults(string $objectType, array $crmIds, array $results): void
{
$this->log->info("[HubSpot] Batch fetched {$objectType}", [
'requested_count' => count($crmIds),
'returned_count' => count($results),
'crm_ids' => $crmIds,
]);
}
private function handleBatchError(\Throwable $e, string $objectType, array $crmIds): void
{
$errorMessage = $e->getMessage() ?: 'Unknown error';
$errorTrace = $e->getTraceAsString() ?: 'No trace available';
$this->log->error("[HubSpot] Failed to batch fetch {$objectType}", [
'crm_ids' => $crmIds,
'error' => $errorMessage,
'trace' => $errorTrace,
]);
throw new CrmException("Failed to batch fetch {$objectType}: " . $errorMessage);
}
/**
* Batch read multiple opportunities by their CRM IDs
*
* @param array<string> $crmIds Array of HubSpot deal IDs (max 100)
* @param array<string> $fields Array of property names to fetch
*
* @return array<string, array> Array keyed by CRM ID with opportunity data
*/
public function getOpportunitiesByIds(array $crmIds, array $fields): array
{
return $this->batchReadObjects('deals', $crmIds, $fields);
}
/**
* Batch read multiple companies by their CRM IDs
*
* @param array<string> $crmIds Array of HubSpot company IDs (max 100)
* @param array<string> $fields Array of property names to fetch
*
* @return array<string, array> Array keyed by CRM ID with company data
*/
public function getCompaniesByIds(array $crmIds, array $fields): array
{
return $this->batchReadObjects('companies', $crmIds, $fields);
}
/**
* Batch read multiple contacts by their CRM IDs
*
* @param array<string> $crmIds Array of HubSpot contact IDs (max 100)
* @param array<string> $fields Array of property names to fetch
*
* @return array<string, array> Array keyed by CRM ID with contact data
*/
public function getContactsByIds(array $crmIds, array $fields): array
{
return $this->batchReadObjects('contacts', $crmIds, $fields);
}
/**
* @throws CompanyApiException
* @throws CrmException
*/
public function getAccountById(string $crmId, array $fields): array
{
try {
$company = $this->getNewInstance()->crm()->companies()->basicApi()->getById(
$crmId,
implode(',', $fields),
);
} catch (CompanyApiException $e) {
$this->log->info('[Hubspot] Failed to fetch account', [
'crm_id' => $crmId,
'reason' => $e->getMessage(),
]);
throw $e;
}
if (! $company instanceof CompaniesWithAssociations) {
throw new CrmException('Account not found');
}
return [
'id' => $company->getId(),
'properties' => $company->getProperties(),
];
}
/**
* @throws ContactApiException
* @throws CrmException
*/
public function getContactById(string $crmId, array $fields): array
{
try {
$contact = $this->getNewInstance()->crm()->contacts()->basicApi()->getById(
$crmId,
implode(',', $fields)
);
} catch (ContactApiException $e) {
$this->log->info('[Hubspot] Failed to fetch contact', [
'crm_id' => $crmId,
'reason' => $e->getMessage(),
]);
throw $e;
}
if (! $contact instanceof ContactsWithAssociations) {
throw new CrmException('Contact not found');
}
return [
'id' => $contact->getId(),
'properties' => $contact->getProperties(),
];
}
/**
* This is email search request that Hubspot offers as GET (more generous quota)
*/
public function getContactByEmail(string $email, array $fields = []): array
{
try {
$contact = $this->getNewInstance()->crm()->contacts()->basicApi()->getById(
$email,
implode(',', $fields),
null,
false,
'email'
);
return [
'id' => $contact->getId(),
'properties' => $contact->getProperties(),
];
} catch (ContactApiException $e) {
$this->log->info('[Hubspot] Failed to fetch contact', [
'email' => $email,
'reason' => $e->getMessage(),
]);
return [];
}
}
/**
* @throws CrmException
*/
public function fetchProperty(string $objectType, string $propertyId): Property
{
$result = $this->getNewInstance()->crm()->properties()->coreApi()->getByName($objectType, $propertyId);
if (! $result instanceof Property) {
$this->log->error('[Hubspot] Failed to fetch property', [
'object_type' => $objectType,
'property_id' => $propertyId,
'reason' => $result->getMessage(),
]);
throw new CrmException('Failed to fetch property');
}
return $result;
}
/**
* @return array<CrmFieldOption>
*/
public function fetchPropertyOptions(string $objectType, string $propertyId): array
{
/** @var array<CrmFieldOption> */
return $this->fetchProperty($objectType, $propertyId)->getOptions();
}
/**
* @return array<array{id:string, label:string, deleted:bool}>
*/
public function fetchCallDispositions(): array
{
/** @var Response $response */
$response = $this->getInstance()->engagements()->getCallDispositions();
/**
* @var array<array{
* id:string,
* label:string,
* deleted: bool
* }>
*/
return $response->toArray();
}
/**
* @return array<CrmFieldOption>
*/
public function fetchOpportunityPipelineStages(): array
{
$stages = [];
$apiResponse = $this->getNewInstance()->crm()->pipelines()->pipelinesApi()->getAll('deals');
if ($apiResponse instanceof Error) {
$this->log->error('[Hubspot] Failed to fetch opportunity pipelines', [
'reason' => $apiResponse->getMessage(),
]);
return [];
}
foreach ($apiResponse->getResults() as $pipeline) {
$pipelineStages = array_map(
static function (PipelineStage $stage) {
return [
'id' => $stage->getId(),
'label' => $stage->getLabel(),
];
},
$pipeline->getStages()
);
$stages = array_merge($stages, $pipelineStages);
}
return $stages;
}
public function fetchOpportunityPipelines(): array
{
$pipelines = [];
try {
$apiResponse = $this->makeRequest('/crm/v3/pipelines/deals');
} catch (\Exception $e) {
$this->log->info('[Hubspot] Failed to fetch opportunity pipelines', [
'reason' => $e->getMessage(),
]);
return [];
}
$response = $apiResponse->toArray();
foreach ($response['results'] as $pipeline) {
$pipelines[] = [
'id' => $pipeline['id'],
'label' => $pipeline['label'],
];
}
return $pipelines;
}
/**
* @return array<CrmFieldOption>
*/
public function fetchMeetingOutcomeFieldOptions(Field $field): array
{
return $field->getCrmProviderId() === 'meetingOutcome'
? $this->fetchMeetingOutcomeTypes()
: $this->fetchCallActivityTypes();
}
public function fetchMeetingOutcomeTypes(): array
{
return $this->extractMeetingTypeOptions(
'[URL_WITH_CREDENTIALS] Response $response */
$response = $this->getInstance()
->getClient()
->request('GET', $endpoint);
/**
* @var array<array{
* value: string,
* label: string,
* displayOrder: int
* }> $optionData
*/
$optionData = $response->toArray()['options'] ?? [];
$options = [];
foreach ($optionData as $item) {
$options[] = [
'id' => $item['value'],
'value' => $item['value'],
'label' => $item['label'],
'display_order' => $item['displayOrder'],
];
}
return $options;
}
/**
* @return array<CrmFieldOption>
*/
public function fetchDispositionFieldOptions(): array
{
$options = [];
$dispositions = $this->fetchCallDispositions();
foreach ($dispositions as $disposition) {
if ($disposition['deleted'] !== false) {
continue;
}
$option['value'] = $disposition['id'];
$option['id'] = $disposition['id'];
$option['label'] = $disposition['label'];
$options[] = $option;
}
return $options;
}
/**
* @return array<CrmFieldOption>
*/
public function fetchOpportunityFieldOptions(Field $field): array
{
if ($field->isStageField()) {
return $this->fetchOpportunityPipelineStages();
}
if ($field->isPipelineField()) {
return $this->fetchOpportunityPipelines();
}
return $this->fetchPropertyOptions('deals', $field->getCrmProviderId());
}
/**
* @throws BadRequest
* @throws HubspotException
*/
public function makeRequest(string $endpoint, $method = 'GET', $payload = [], ?string $queryString = null)
{
$endpoint = self::BASE_URL . $endpoint;
if ($method === 'GET') {
return $this->getInstance()->getClient()?->request(
method: $method,
endpoint: $endpoint,
query_string: $queryString
);
} else {
return $this->getInstance()->getClient()->request($method, $endpoint, [
'json' => ($payload),
]);
}
}
/**
* @throws BadRequest
* @throws HubspotException
*/
public function createMeeting(array $payload): Response
{
$endpoint = '/crm/v3/objects/meetings';
return $this->makeRequest($endpoint, 'POST', $payload);
}
/**
* @throws BadRequest
* @throws HubspotException
*/
public function updateMeeting(string $meetingId, array $payload): Response
{
$endpoint = '/crm/v3/objects/meetings/' . $meetingId;
return $this->makeRequest($endpoint, 'PATCH', $payload);
}
/**
* @throws \Exception
*/
public function createNote(
string $body,
string $ownerId,
int $timestamp,
string $objectId,
NoteObject $noteObject
): ?string {
try {
$noteInput = new SimplePublicObjectInput([
'properties' => [
'hs_note_body' => $body,
'hubspot_owner_id' => $ownerId,
'hs_timestamp' => $timestamp,
],
]);
// Create note
$note = $this->getNewInstance()->crm()->objects()->basicApi()->create('note', $noteInput);
$this->getNewInstance()->crm()->objects()->associationsApi()->create(
'note',
$note->getId(),
$this->getNoteObject($noteObject),
$objectId,
$this->getNoteAssociationType($noteObject),
);
return $note->getId();
} catch (\Exception $e) {
$this->log->error('[Hubspot] Failed to create note', [
'objectId' => $objectId,
'noteObject' => $noteObject->getObjectType(),
'reason' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
return null;
}
public function updateEngagement(string $objectId, array $engagement, array $metadata): void
{
$this->getInstance()->engagements()->update($objectId, $engagement, $metadata);
}
public function getEngagementData(string $engagementId): array
{
$engagement = $this->getInstance()->engagements()->get($engagementId);
return $engagement->toArray();
}
public function createEngagement(array $engagement, array $associations, array $metadata): Response
{
return $this->getInstance()
->engagements()
->create($engagement, $associations, $metadata);
}
public function isUnauthorizedException(\Exception $e): bool
{
// Check for specific HubSpot API exception types first
if ($e instanceof BadRequest) {
// BadRequest can contain 401 status codes
return $e->getCode() === 401;
}
// Check for HTTP client exceptions with status codes
if ($e instanceof \GuzzleHttp\Exception\RequestException && $e->hasResponse()) {
$response = $e->getResponse();
if ($response !== null) {
return $response->getStatusCode() === 401;
}
}
// Check for Guzzle HTTP exceptions
if ($e instanceof \GuzzleHttp\Exception\ClientException) {
return $e->getCode() === 401;
}
// Fallback to string matching as last resort, but be more specific
$message = strtolower($e->getMessage());
return str_contains($message, '401 unauthorized') ||
str_contains($message, 'http 401') ||
str_contains($message, 'status code 401') ||
(preg_match('/\b401\b/', $message) && str_contains($message, 'unauthorized'));
}
/**
* Validates and refreshes the access token if needed before API requests.
* This ensures long-running processes don't fail due to token expiration.
*
* @throws SocialAccountTokenInvalidException
*/
public function ensureValidToken(): void
{
if ($this->oauthAccount === null) {
return;
}
$newToken = $this->tokenManager->ensureValidToken($this->oauthAccount);
if ($newToken !== null) {
$this->accessToken = $newToken;
}
}
public function getConfig()
{
return $this->config;
}
// returns only active (archived=false)
public function getOwners(): array
{
return $this->getNewInstance()->crm()->owners()->getAll();
}
/**
* @param bool $archived
*
* @return array<Owner>|[]
*/
public function getOwnersArchived(bool $archived = true): array
{
$endpoint = '/crm/v3/owners';
$queryParams = [
'archived' => $archived ? 'true' : 'false',
];
$queryString = http_build_query($queryParams);
$owners = [];
try {
$response = $this->makeRequest(endpoint: $endpoint, queryString: $queryString);
$responseData = $response?->toArray();
foreach ($responseData['results'] as $result) {
try {
$owners[] = Owner::create($result);
} catch (Throwable $e) {
$this->log->error('[HubSpot] Failed to process owner data', [
'result' => $result,
'error' => $e->getMessage(),
]);
continue;
}
}
} catch (Throwable $e) {
$this->log->error('HubSpot] Failed to fetch owners', [
'archived' => $archived,
'error' => $e->getMessage(),
]);
return [];
}
return $owners;
}
public function getMeeting(string $engagementId): ObjectWithAssociations
{
return $this->getNewInstance()->crm()->objects()->basicApi()
->getById('meeting', $engagementId, null, 'contact,company,deal');
}
public function deleteEngagement(string $engagementId): void
{
$this->getInstance()->engagements()->delete((int) $engagementId);
}
public function getAssociationsData(array $ids, string $fromObject, string $toObject): array
{
$associationData = [];
$idChunks = array_chunk($ids, self::ASSOCIATIONS_BATCH_SIZE_LIMIT);
foreach ($idChunks as $idChunk) {
try {
$batchInput = new \HubSpot\Client\Crm\Associations\Model\BatchInputPublicObjectId();
$batchInput->setInputs(array_map(function ($id) {
$publicObjectId = new \HubSpot\Client\Crm\Associations\Model\PublicObjectId();
$publicObjectId->setId($id);
return $publicObjectId;
}, $idChunk));
$associatedObjectsData = $this
->getNewInstance()
->crm()
->associations()
->batchApi()
->read($fromObject, $toObject, $batchInput);
if ($associatedObjectsData instanceof \HubSpot\Client\Crm\Associations\Model\BatchResponsePublicAssociationMulti) {
foreach ($associatedObjectsData->getResults() as $association) {
$from = $association->getFrom()->getId();
$toAssociations = $association->getTo();
if (! empty($toAssociations)) {
$associationData[$from] = array_map(function ($item) {
return $item->getId();
}, $toAssociations);
}
}
}
// } catch (RateLimitException $e) {
// throw $e;
} catch (\Exception $e) {
$this->log->error('[Hubspot] Failed to fetch associations', [
'from_object' => $fromObject,
'to_object' => $toObject,
'reason' => $e->getMessage(),
]);
}
}
return $associationData;
}
/**
* @throws \Exception
*/
private function getNoteAssociationType(NoteObject $noteObject): string
{
return match($noteObject) {
NoteObject::Opportunity => 'note_to_deal',
NoteObject::Lead, NoteObject::Contact => 'note_to_contact', // or 'note_to_lead' if your portal supports it
NoteObject::Account => 'note_to_company',
NoteObject::Call, NoteObject::Event => throw new \Exception('Not supported'),
};
}
/**
* @throws \Exception
*/
private function getNoteObject(NoteObject $noteObject): string
{
return match($noteObject) {
NoteObject::Opportunity => 'deal',
NoteObject::Lead, NoteObject::Contact => 'contact',
NoteObject::Account => 'company',
NoteObject::Call, NoteObject::Event => throw new \Exception('Not supported'),
};
}
public function addAssociations(string $objectType, string $associationType, array $payload): Response
{
$endpoint = "/crm/v4/associations/$objectType/$associationType/batch/create";
return $this->makeRequest($endpoint, 'POST', $payload);
}
public function removeAssociations(string $objectType, string $associationType, array $payload): Response
{
$endpoint = "/crm/v4/associations/$objectType/$associationType/batch/archive";
return $this->makeRequest($endpoint, 'POST', $payload);
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
19
Previous Highlighted Error
Next Highlighted Error
[2026-05-07 14:21:15] local.INFO: [Hubspot] DEBUG Getting headers {
"headers":{
"Date":["Thu,07 May 2026 14:21:15 GMT"],
"Content-Type":["application/json;charset=utf-8"],
"Transfer-Encoding":["chunked"],
"Connection":["keep-alive"],
"CF-Ray":["9f80deb8db60dc3a-SOF"],
"CF-Cache-Status":["DYNAMIC"],
"Strict-Transport-Security":["max-age=31536000; includeSubDomains; preload"],
"Vary":["origin,
accept-encoding"],
"access-control-allow-credentials":["false"],
"server-timing":["hcid;desc=\"019e02d0-6fd8-7812-bdba-885b7ccb3ee3\",
cfr;desc=\"9f80deb8e7c6dc3a-IAD\""],
"x-content-type-options":["nosniff"],
"x-hubspot-correlation-id":["019e02d0-6fd8-7812-bdba-885b7ccb3ee3"],
"Set-Cookie":["__cf_bm=SIUrtdQgXVrik50pdqF6hZVYKhzTnQBidvMabeCtm0Y-1778163675-[IP_ADDRESS]-rI.ZggtDKxTge5zr8_2gbBfWMQQ.ufZEXDZyHz2mBUFdzdo2gTHEsOkXMSEShjK0hGYxNhUGM1ZoBpX7BcFZcHEjA7Cs_.SMUhUnd2nYjko; path=/; expires=Thu,
07-May-26 14:51:15 GMT; domain=.hubapi.com; HttpOnly; Secure; SameSite=None"],
"Report-To":["{
\"endpoints\":[{
\"url\":\"https:\\/\\/a.nel.cloudflare.com\\/report\\/v4?s=NYAlsVTP0fYm32qrSDjxYE4sd2RWRqiSp3wHsmdEgZlzoYdxI%2BIxVpHmsKn3O%2BKVA3mFIJ2m7YRECDGSM%2BW2IYTzo6FM4%2BdUIjURO8srzKSvJgZ%2BQ6R79arKQw3uHLlX\"}],
\"group\":\"cf-nel\",
\"max_age\":604800}"],
"NEL":["{
\"success_fraction\":0.01,
\"report_to\":\"cf-nel\",
\"max_age\":604800}"],
"Server":["cloudflare"]}} {
"correlation_id":"95236535-ec98-4541-b92a-adfa73b69eab",
"trace_id":"c7ab8365-903f-46d4-9403-0e5b551e3545"}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JY-20725-handle-HS-search-rate-limit, menu","depth":5,"on_screen":true,"help_text":"Git Branch: JY-20725-handle-HS-search-rate-limit","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"HandleHubspotRateLimitTest","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'HandleHubspotRateLimitTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'HandleHubspotRateLimitTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"2","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"67","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"3","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Services\\Crm\\Hubspot;\n\nuse HubSpot\\Client\\Crm\\Deals\\ApiException as DealApiException;\nuse HubSpot\\Client\\Crm\\Contacts\\ApiException as ContactApiException;\nuse HubSpot\\Client\\Crm\\Companies\\ApiException as CompanyApiException;\nuse HubSpot\\Client\\Crm\\Contacts\\Model\\SimplePublicObjectWithAssociations as ContactsWithAssociations;\nuse HubSpot\\Client\\Crm\\Companies\\Model\\SimplePublicObjectWithAssociations as CompaniesWithAssociations;\nuse HubSpot\\Client\\Crm\\Deals\\Model\\SimplePublicObjectWithAssociations as DealWithAssociations;\nuse HubSpot\\Client\\Crm\\Objects\\Model\\SimplePublicObjectInput;\nuse HubSpot\\Client\\Crm\\Objects\\Model\\SimplePublicObjectWithAssociations as ObjectWithAssociations;\nuse HubSpot\\Client\\Crm\\Pipelines\\Model\\Error;\nuse HubSpot\\Client\\Crm\\Pipelines\\Model\\PipelineStage;\nuse HubSpot\\Client\\Crm\\Properties\\Model\\Property;\nuse HubSpot\\Discovery\\Discovery;\nuse Jiminny\\Exceptions\\CrmException;\nuse Jiminny\\Exceptions\\RateLimitException;\nuse Jiminny\\Exceptions\\SocialAccountTokenInvalidException;\nuse Jiminny\\Jobs\\Crm\\NoteObject;\nuse Jiminny\\Models\\Crm\\Field;\nuse Jiminny\\Services\\Crm\\BaseClient;\nuse Jiminny\\Services\\Crm\\Hubspot\\DTO\\Response\\Owner;\nuse Jiminny\\Services\\SocialAccountService;\nuse SevenShores\\Hubspot\\Exceptions\\BadRequest;\nuse SevenShores\\Hubspot\\Exceptions\\HubspotException;\nuse SevenShores\\Hubspot\\Factory;\nuse SevenShores\\Hubspot\\Http\\Response;\nuse Jiminny\\Services\\Crm\\Hubspot\\Pagination\\HubspotPaginationService;\nuse Illuminate\\Support\\Facades\\Redis;\nuse Throwable;\n\n/**\n * @phpstan-type CrmFieldOption array{id:string, label:string, value?:string}\n */\nclass Client extends BaseClient implements HubspotClientInterface\n{\n public const string MIN_API_VERSION = '2';\n\n public const string BASE_URL = 'https://api.hubapi.com';\n\n public const int ASSOCIATIONS_BATCH_SIZE_LIMIT = 1000;\n\n private HubspotPaginationService $paginationService;\n private HubspotTokenManager $tokenManager;\n\n public function __construct(\n SocialAccountService $socialAccountService,\n HubspotPaginationService $paginationService,\n HubspotTokenManager $tokenManager\n ) {\n parent::__construct($socialAccountService);\n $this->paginationService = $paginationService;\n $this->tokenManager = $tokenManager;\n\n $this->setBaseUrl(self::BASE_URL);\n $this->setVersion(self::MIN_API_VERSION);\n }\n\n /**\n * Reacts to a rate limits (429) from HubSpot by translating it\n * into a RateLimitException carrying retry_after.\n *\n * Wrap any outbound HubSpot call (SDK or raw HTTP) like:\n *\n * $this->executeRequest(fn () => $this->getNewInstance()->crm()->...);\n *\n * @template T\n * @param callable(): T $apiCall\n * @return T\n *\n * @throws RateLimitException\n */\n private function executeRequest(callable $apiCall)\n {\n $cacheKey = $this->getRateLimitCacheKey();\n\n $cachedRetryAfter = Redis::get($cacheKey);\n if (is_string($cachedRetryAfter) && is_numeric($cachedRetryAfter)) {\n throw new RateLimitException(\n 'Hubspot rate limit (cached circuit-breaker)',\n (int) $cachedRetryAfter,\n );\n }\n\n try {\n return $apiCall();\n } catch (Throwable $e) {\n if ($this->isHubspotRateLimit($e)) {\n $retryAfter = $this->parseRetryAfter($e);\n\n Redis::setex($cacheKey, $retryAfter, (string) $retryAfter);\n\n $this->log->warning('[Hubspot] Received 429 from API', [\n 'team_id' => $this->config->team_id,\n 'config_id' => $this->config->getId(),\n 'retry_after' => $retryAfter,\n 'message' => $this->parseMessage($e),\n 'reason' => $e->getMessage(),\n ]);\n\n throw new RateLimitException('Hubspot returned 429', $retryAfter, $e);\n }\n\n throw $e;\n }\n }\n\n private function getRateLimitCacheKey(): string\n {\n return sprintf('hubspot:ratelimit:portal:%d', $this->config->getId());\n }\n\n public function isHubspotRateLimit(Throwable $e): bool\n {\n if ($e instanceof BadRequest\n || $e instanceof DealApiException\n || $e instanceof ContactApiException\n || $e instanceof CompanyApiException\n || $e instanceof \\GuzzleHttp\\Exception\\RequestException\n ) {\n return (int) $e->getCode() === 429;\n }\n\n return false;\n }\n\n public function parseRetryAfter(Throwable $e): int\n {\n if (method_exists($e, 'getResponseHeaders')) {\n $headers = $e->getResponseHeaders() ?: [];\n $value = $headers['Retry-After'] ?? $headers['retry-after'] ?? null;\n if (is_array($value)) {\n $value = $value[0] ?? null;\n }\n if (is_numeric($value)) {\n return (int) $value;\n }\n }\n\n $message = strtolower($this->parseMessage($e));\n\n if (str_contains($message, 'daily')) {\n return 600;\n }\n if (str_contains($message, 'ten secondly')) {\n return 10;\n }\n if (str_contains($message, 'secondly')) {\n return 1;\n }\n\n $this->log->warning('[Hubspot] No retry-after header or known message, using default', [\n 'exception_class' => get_class($e),\n 'message' => $message,\n ]);\n\n return 10;\n }\n\n public function parseMessage(Throwable $e): string\n {\n if (! method_exists($e, 'getResponseBody')) {\n return '';\n }\n\n $body = $e->getResponseBody();\n if (is_string($body)) {\n $body = json_decode($body, true) ?? [];\n }\n\n if (! is_array($body)) {\n return '';\n }\n\n return (string) ($body['message'] ?? '');\n }\n\n public function getMinimumApiVersion(): string\n {\n return self::MIN_API_VERSION;\n }\n\n public function getInstance(): Factory\n {\n return new Factory([\n 'key' => $this->accessToken,\n 'oauth2' => true,\n 'base_url' => $this->baseUrl,\n ]);\n }\n\n public function getNewInstance(): Discovery\n {\n return \\HubSpot\\Factory::createWithAccessToken($this->accessToken);\n }\n\n /**\n * Secondly and daily limits for Hubspot API\n *\n * Product Tier: Free & Starter | Professional & Enterprise | API add-on (any tier)\n * Burst: 100/10 seconds | 150/10 seconds | 200/10 seconds\n * Daily: 250,000 | 500,000 | 1,000,000\n *\n * Official documentation states: The search endpoints are rate limited to five requests per second.\n * Since with 5 RPS were still hitting secondly rate limits we lowered it to 4\n */\n public function getPaginatedData(array $payload, string $type, int $offset = 0): array\n {\n $total = 0;\n $lastId = null;\n $rows = [];\n foreach ($this->getPaginatedDataGenerator($payload, $type, $offset, $total, $lastId) as $row) {\n $rows[] = $row;\n }\n\n return ['results' => $rows, 'total' => $total, 'last_record' => $lastId];\n }\n\n /**\n * @throws HubspotException\n * @throws SocialAccountTokenInvalidException\n * @throws BadRequest\n */\n public function getPaginatedDataGenerator(\n array $payload,\n string $type,\n int $offset = 0,\n int &$total = 0,\n ?string &$lastRecordId = null\n ): \\Generator {\n return $this->paginationService->getPaginatedDataGenerator(\n $this,\n $payload,\n $type,\n $offset,\n $total,\n $lastRecordId\n );\n }\n\n /**\n * Execute a search request against HubSpot CRM objects with rate limiting.\n *\n * @param string $objectType The object type ('deals', 'companies', 'contacts', 'calls')\n * @param array<string, mixed> $payload The search payload with filters, sorts, properties, etc.\n * @return array The search response with 'results', 'total', 'paging' keys\n * @throws RateLimitException When rate limit is hit\n * @throws HubspotException On API errors\n */\n public function search(string $objectType, array $payload): array\n {\n $endpoint = self::BASE_URL . \"/crm/v3/objects/{$objectType}/search\";\n\n return $this->executeRequest(function () use ($endpoint, $payload) {\n $response = $this->getInstance()->getClient()->request('POST', $endpoint, ['json' => $payload]);\n\n return $response->toArray();\n });\n }\n\n /**\n * @throws DealApiException\n * @throws CrmException\n */\n public function getOpportunityById(string $crmId, array $fields): array\n {\n try {\n// $deal = $this->executeRequest(fn () => $this->getNewInstance()->crm()->deals()->basicApi()->getById(\n $deal = $this->getNewInstance()->crm()->deals()->basicApi()->getById(\n $crmId,\n implode(',', $fields),\n 'companies,contacts'\n );\n } catch (DealApiException $e) {\n $this->log->info('[Hubspot] Failed to fetch opportunity', [\n 'crm_id' => $crmId,\n 'reason' => $e->getMessage(),\n ]);\n\n throw $e;\n }\n\n if (! $deal instanceof DealWithAssociations) {\n throw new CrmException('Deal not found');\n }\n\n return [\n 'id' => $deal->getId(),\n 'properties' => $deal->getProperties(),\n 'associations' => $deal->getAssociations(),\n ];\n }\n\n /**\n * Generic batch read method for HubSpot objects\n *\n * @param string $objectType The object type ('deals', 'companies', 'contacts')\n * @param array<string> $crmIds Array of HubSpot object IDs (max 100)\n * @param array<string> $fields Array of property names to fetch\n *\n * @return array<string, array> Array keyed by CRM ID with object data\n */\n private function batchReadObjects(string $objectType, array $crmIds, array $fields): array\n {\n if (empty($crmIds)) {\n return [];\n }\n\n $this->validateBatchSize($objectType, $crmIds);\n $this->ensureValidToken();\n\n try {\n $batchConfig = $this->createBatchConfiguration($objectType);\n $batchReadRequest = $this->prepareBatchRequest($batchConfig, $crmIds, $fields);\n $response = $batchConfig['api']->read($batchReadRequest);\n\n $this->validateApiResponse($response, $objectType);\n\n $results = $this->processApiResults($response);\n $this->logBatchResults($objectType, $crmIds, $results);\n\n return $results;\n } catch (\\Throwable $e) {\n $this->handleBatchError($e, $objectType, $crmIds);\n }\n }\n\n private function validateBatchSize(string $objectType, array $crmIds): void\n {\n if (count($crmIds) > 100) {\n throw new \\InvalidArgumentException(\"Batch size cannot exceed 100 {$objectType}\");\n }\n }\n\n private function createBatchConfiguration(string $objectType): array\n {\n $configurations = [\n 'deals' => [\n 'batchReadRequest' => new \\HubSpot\\Client\\Crm\\Deals\\Model\\BatchReadInputSimplePublicObjectId(),\n 'inputClass' => \\HubSpot\\Client\\Crm\\Deals\\Model\\SimplePublicObjectId::class,\n 'api' => $this->getNewInstance()->crm()->deals()->batchApi(),\n ],\n 'companies' => [\n 'batchReadRequest' => new \\HubSpot\\Client\\Crm\\Companies\\Model\\BatchReadInputSimplePublicObjectId(),\n 'inputClass' => \\HubSpot\\Client\\Crm\\Companies\\Model\\SimplePublicObjectId::class,\n 'api' => $this->getNewInstance()->crm()->companies()->batchApi(),\n ],\n 'contacts' => [\n 'batchReadRequest' => new \\HubSpot\\Client\\Crm\\Contacts\\Model\\BatchReadInputSimplePublicObjectId(),\n 'inputClass' => \\HubSpot\\Client\\Crm\\Contacts\\Model\\SimplePublicObjectId::class,\n 'api' => $this->getNewInstance()->crm()->contacts()->batchApi(),\n ],\n ];\n\n if (! isset($configurations[$objectType])) {\n throw new \\InvalidArgumentException(\"Unsupported object type: {$objectType}\");\n }\n\n return $configurations[$objectType];\n }\n\n private function prepareBatchRequest(array $batchConfig, array $crmIds, array $fields): object\n {\n $batchReadRequest = $batchConfig['batchReadRequest'];\n $inputClass = $batchConfig['inputClass'];\n\n $inputs = array_map(function ($crmId) use ($inputClass) {\n $input = new $inputClass();\n $input->setId($crmId);\n\n return $input;\n }, $crmIds);\n\n $batchReadRequest->setInputs($inputs);\n $batchReadRequest->setProperties($fields);\n\n return $batchReadRequest;\n }\n\n private function validateApiResponse($response, string $objectType): void\n {\n if (! $response) {\n throw new CrmException(\"HubSpot API returned null response for {$objectType} batch read\");\n }\n }\n\n private function processApiResults($response): array\n {\n $results = [];\n $responseResults = $response->getResults();\n\n if ($responseResults) {\n foreach ($responseResults as $object) {\n if ($object && $object->getId()) {\n $results[$object->getId()] = [\n 'id' => $object->getId(),\n 'properties' => $object->getProperties() ?: [],\n ];\n }\n }\n }\n\n return $results;\n }\n\n private function logBatchResults(string $objectType, array $crmIds, array $results): void\n {\n $this->log->info(\"[HubSpot] Batch fetched {$objectType}\", [\n 'requested_count' => count($crmIds),\n 'returned_count' => count($results),\n 'crm_ids' => $crmIds,\n ]);\n }\n\n private function handleBatchError(\\Throwable $e, string $objectType, array $crmIds): void\n {\n $errorMessage = $e->getMessage() ?: 'Unknown error';\n $errorTrace = $e->getTraceAsString() ?: 'No trace available';\n\n $this->log->error(\"[HubSpot] Failed to batch fetch {$objectType}\", [\n 'crm_ids' => $crmIds,\n 'error' => $errorMessage,\n 'trace' => $errorTrace,\n ]);\n\n throw new CrmException(\"Failed to batch fetch {$objectType}: \" . $errorMessage);\n }\n\n /**\n * Batch read multiple opportunities by their CRM IDs\n *\n * @param array<string> $crmIds Array of HubSpot deal IDs (max 100)\n * @param array<string> $fields Array of property names to fetch\n *\n * @return array<string, array> Array keyed by CRM ID with opportunity data\n */\n public function getOpportunitiesByIds(array $crmIds, array $fields): array\n {\n return $this->batchReadObjects('deals', $crmIds, $fields);\n }\n\n /**\n * Batch read multiple companies by their CRM IDs\n *\n * @param array<string> $crmIds Array of HubSpot company IDs (max 100)\n * @param array<string> $fields Array of property names to fetch\n *\n * @return array<string, array> Array keyed by CRM ID with company data\n */\n public function getCompaniesByIds(array $crmIds, array $fields): array\n {\n return $this->batchReadObjects('companies', $crmIds, $fields);\n }\n\n /**\n * Batch read multiple contacts by their CRM IDs\n *\n * @param array<string> $crmIds Array of HubSpot contact IDs (max 100)\n * @param array<string> $fields Array of property names to fetch\n *\n * @return array<string, array> Array keyed by CRM ID with contact data\n */\n public function getContactsByIds(array $crmIds, array $fields): array\n {\n return $this->batchReadObjects('contacts', $crmIds, $fields);\n }\n\n /**\n * @throws CompanyApiException\n * @throws CrmException\n */\n public function getAccountById(string $crmId, array $fields): array\n {\n try {\n $company = $this->getNewInstance()->crm()->companies()->basicApi()->getById(\n $crmId,\n implode(',', $fields),\n );\n } catch (CompanyApiException $e) {\n $this->log->info('[Hubspot] Failed to fetch account', [\n 'crm_id' => $crmId,\n 'reason' => $e->getMessage(),\n ]);\n\n throw $e;\n }\n\n if (! $company instanceof CompaniesWithAssociations) {\n throw new CrmException('Account not found');\n }\n\n return [\n 'id' => $company->getId(),\n 'properties' => $company->getProperties(),\n ];\n }\n\n /**\n * @throws ContactApiException\n * @throws CrmException\n */\n public function getContactById(string $crmId, array $fields): array\n {\n try {\n $contact = $this->getNewInstance()->crm()->contacts()->basicApi()->getById(\n $crmId,\n implode(',', $fields)\n );\n } catch (ContactApiException $e) {\n $this->log->info('[Hubspot] Failed to fetch contact', [\n 'crm_id' => $crmId,\n 'reason' => $e->getMessage(),\n ]);\n\n throw $e;\n }\n\n if (! $contact instanceof ContactsWithAssociations) {\n throw new CrmException('Contact not found');\n }\n\n return [\n 'id' => $contact->getId(),\n 'properties' => $contact->getProperties(),\n ];\n }\n\n /**\n * This is email search request that Hubspot offers as GET (more generous quota)\n */\n public function getContactByEmail(string $email, array $fields = []): array\n {\n try {\n $contact = $this->getNewInstance()->crm()->contacts()->basicApi()->getById(\n $email,\n implode(',', $fields),\n null,\n false,\n 'email'\n );\n\n return [\n 'id' => $contact->getId(),\n 'properties' => $contact->getProperties(),\n ];\n } catch (ContactApiException $e) {\n $this->log->info('[Hubspot] Failed to fetch contact', [\n 'email' => $email,\n 'reason' => $e->getMessage(),\n ]);\n\n return [];\n }\n }\n\n /**\n * @throws CrmException\n */\n public function fetchProperty(string $objectType, string $propertyId): Property\n {\n $result = $this->getNewInstance()->crm()->properties()->coreApi()->getByName($objectType, $propertyId);\n\n if (! $result instanceof Property) {\n $this->log->error('[Hubspot] Failed to fetch property', [\n 'object_type' => $objectType,\n 'property_id' => $propertyId,\n 'reason' => $result->getMessage(),\n ]);\n\n throw new CrmException('Failed to fetch property');\n }\n\n return $result;\n }\n\n /**\n * @return array<CrmFieldOption>\n */\n public function fetchPropertyOptions(string $objectType, string $propertyId): array\n {\n /** @var array<CrmFieldOption> */\n return $this->fetchProperty($objectType, $propertyId)->getOptions();\n }\n\n /**\n * @return array<array{id:string, label:string, deleted:bool}>\n */\n public function fetchCallDispositions(): array\n {\n /** @var Response $response */\n $response = $this->getInstance()->engagements()->getCallDispositions();\n\n /**\n * @var array<array{\n * id:string,\n * label:string,\n * deleted: bool\n * }>\n */\n return $response->toArray();\n }\n\n /**\n * @return array<CrmFieldOption>\n */\n public function fetchOpportunityPipelineStages(): array\n {\n $stages = [];\n $apiResponse = $this->getNewInstance()->crm()->pipelines()->pipelinesApi()->getAll('deals');\n\n if ($apiResponse instanceof Error) {\n $this->log->error('[Hubspot] Failed to fetch opportunity pipelines', [\n 'reason' => $apiResponse->getMessage(),\n ]);\n\n return [];\n }\n\n foreach ($apiResponse->getResults() as $pipeline) {\n $pipelineStages = array_map(\n static function (PipelineStage $stage) {\n return [\n 'id' => $stage->getId(),\n 'label' => $stage->getLabel(),\n ];\n },\n $pipeline->getStages()\n );\n\n $stages = array_merge($stages, $pipelineStages);\n }\n\n return $stages;\n }\n\n public function fetchOpportunityPipelines(): array\n {\n $pipelines = [];\n\n try {\n $apiResponse = $this->makeRequest('/crm/v3/pipelines/deals');\n } catch (\\Exception $e) {\n $this->log->info('[Hubspot] Failed to fetch opportunity pipelines', [\n 'reason' => $e->getMessage(),\n ]);\n\n return [];\n }\n\n $response = $apiResponse->toArray();\n\n foreach ($response['results'] as $pipeline) {\n $pipelines[] = [\n 'id' => $pipeline['id'],\n 'label' => $pipeline['label'],\n ];\n }\n\n return $pipelines;\n }\n\n /**\n * @return array<CrmFieldOption>\n */\n public function fetchMeetingOutcomeFieldOptions(Field $field): array\n {\n return $field->getCrmProviderId() === 'meetingOutcome'\n ? $this->fetchMeetingOutcomeTypes()\n : $this->fetchCallActivityTypes();\n }\n\n public function fetchMeetingOutcomeTypes(): array\n {\n return $this->extractMeetingTypeOptions(\n 'https://api.hubapi.com/crm/v3/properties/meeting/hs_meeting_outcome'\n );\n }\n\n public function fetchCallActivityTypes(): array\n {\n return $this->extractMeetingTypeOptions(\n 'https://api.hubapi.com/crm/v3/properties/call/hs_activity_type'\n );\n }\n\n private function extractMeetingTypeOptions(string $endpoint): array\n {\n /** @var Response $response */\n $response = $this->getInstance()\n ->getClient()\n ->request('GET', $endpoint);\n\n /**\n * @var array<array{\n * value: string,\n * label: string,\n * displayOrder: int\n * }> $optionData\n */\n $optionData = $response->toArray()['options'] ?? [];\n\n $options = [];\n foreach ($optionData as $item) {\n $options[] = [\n 'id' => $item['value'],\n 'value' => $item['value'],\n 'label' => $item['label'],\n 'display_order' => $item['displayOrder'],\n ];\n }\n\n return $options;\n }\n\n /**\n * @return array<CrmFieldOption>\n */\n public function fetchDispositionFieldOptions(): array\n {\n $options = [];\n\n $dispositions = $this->fetchCallDispositions();\n\n foreach ($dispositions as $disposition) {\n if ($disposition['deleted'] !== false) {\n continue;\n }\n\n $option['value'] = $disposition['id'];\n $option['id'] = $disposition['id'];\n $option['label'] = $disposition['label'];\n\n $options[] = $option;\n }\n\n return $options;\n }\n\n /**\n * @return array<CrmFieldOption>\n */\n public function fetchOpportunityFieldOptions(Field $field): array\n {\n if ($field->isStageField()) {\n return $this->fetchOpportunityPipelineStages();\n }\n\n if ($field->isPipelineField()) {\n return $this->fetchOpportunityPipelines();\n }\n\n return $this->fetchPropertyOptions('deals', $field->getCrmProviderId());\n }\n\n /**\n * @throws BadRequest\n * @throws HubspotException\n */\n public function makeRequest(string $endpoint, $method = 'GET', $payload = [], ?string $queryString = null)\n {\n $endpoint = self::BASE_URL . $endpoint;\n\n if ($method === 'GET') {\n return $this->getInstance()->getClient()?->request(\n method: $method,\n endpoint: $endpoint,\n query_string: $queryString\n );\n } else {\n return $this->getInstance()->getClient()->request($method, $endpoint, [\n 'json' => ($payload),\n ]);\n }\n }\n\n /**\n * @throws BadRequest\n * @throws HubspotException\n */\n public function createMeeting(array $payload): Response\n {\n $endpoint = '/crm/v3/objects/meetings';\n\n return $this->makeRequest($endpoint, 'POST', $payload);\n }\n\n /**\n * @throws BadRequest\n * @throws HubspotException\n */\n public function updateMeeting(string $meetingId, array $payload): Response\n {\n $endpoint = '/crm/v3/objects/meetings/' . $meetingId;\n\n return $this->makeRequest($endpoint, 'PATCH', $payload);\n }\n\n /**\n * @throws \\Exception\n */\n public function createNote(\n string $body,\n string $ownerId,\n int $timestamp,\n string $objectId,\n NoteObject $noteObject\n ): ?string {\n try {\n $noteInput = new SimplePublicObjectInput([\n 'properties' => [\n 'hs_note_body' => $body,\n 'hubspot_owner_id' => $ownerId,\n 'hs_timestamp' => $timestamp,\n ],\n ]);\n\n // Create note\n $note = $this->getNewInstance()->crm()->objects()->basicApi()->create('note', $noteInput);\n\n $this->getNewInstance()->crm()->objects()->associationsApi()->create(\n 'note',\n $note->getId(),\n $this->getNoteObject($noteObject),\n $objectId,\n $this->getNoteAssociationType($noteObject),\n );\n\n return $note->getId();\n } catch (\\Exception $e) {\n $this->log->error('[Hubspot] Failed to create note', [\n 'objectId' => $objectId,\n 'noteObject' => $noteObject->getObjectType(),\n 'reason' => $e->getMessage(),\n ]);\n\n \\Sentry::captureException($e);\n }\n\n return null;\n }\n\n public function updateEngagement(string $objectId, array $engagement, array $metadata): void\n {\n $this->getInstance()->engagements()->update($objectId, $engagement, $metadata);\n }\n\n public function getEngagementData(string $engagementId): array\n {\n $engagement = $this->getInstance()->engagements()->get($engagementId);\n\n return $engagement->toArray();\n }\n\n public function createEngagement(array $engagement, array $associations, array $metadata): Response\n {\n return $this->getInstance()\n ->engagements()\n ->create($engagement, $associations, $metadata);\n }\n\n public function isUnauthorizedException(\\Exception $e): bool\n {\n // Check for specific HubSpot API exception types first\n if ($e instanceof BadRequest) {\n // BadRequest can contain 401 status codes\n return $e->getCode() === 401;\n }\n\n // Check for HTTP client exceptions with status codes\n if ($e instanceof \\GuzzleHttp\\Exception\\RequestException && $e->hasResponse()) {\n $response = $e->getResponse();\n if ($response !== null) {\n return $response->getStatusCode() === 401;\n }\n }\n\n // Check for Guzzle HTTP exceptions\n if ($e instanceof \\GuzzleHttp\\Exception\\ClientException) {\n return $e->getCode() === 401;\n }\n\n // Fallback to string matching as last resort, but be more specific\n $message = strtolower($e->getMessage());\n\n return str_contains($message, '401 unauthorized') ||\n str_contains($message, 'http 401') ||\n str_contains($message, 'status code 401') ||\n (preg_match('/\\b401\\b/', $message) && str_contains($message, 'unauthorized'));\n }\n\n /**\n * Validates and refreshes the access token if needed before API requests.\n * This ensures long-running processes don't fail due to token expiration.\n *\n * @throws SocialAccountTokenInvalidException\n */\n public function ensureValidToken(): void\n {\n if ($this->oauthAccount === null) {\n return;\n }\n\n $newToken = $this->tokenManager->ensureValidToken($this->oauthAccount);\n if ($newToken !== null) {\n $this->accessToken = $newToken;\n }\n }\n\n public function getConfig()\n {\n return $this->config;\n }\n\n // returns only active (archived=false)\n public function getOwners(): array\n {\n return $this->getNewInstance()->crm()->owners()->getAll();\n }\n\n /**\n * @param bool $archived\n *\n * @return array<Owner>|[]\n */\n public function getOwnersArchived(bool $archived = true): array\n {\n $endpoint = '/crm/v3/owners';\n $queryParams = [\n 'archived' => $archived ? 'true' : 'false',\n ];\n $queryString = http_build_query($queryParams);\n\n $owners = [];\n\n try {\n $response = $this->makeRequest(endpoint: $endpoint, queryString: $queryString);\n $responseData = $response?->toArray();\n\n foreach ($responseData['results'] as $result) {\n try {\n $owners[] = Owner::create($result);\n } catch (Throwable $e) {\n $this->log->error('[HubSpot] Failed to process owner data', [\n 'result' => $result,\n 'error' => $e->getMessage(),\n ]);\n\n continue;\n }\n }\n } catch (Throwable $e) {\n $this->log->error('HubSpot] Failed to fetch owners', [\n 'archived' => $archived,\n 'error' => $e->getMessage(),\n ]);\n\n return [];\n }\n\n return $owners;\n }\n\n public function getMeeting(string $engagementId): ObjectWithAssociations\n {\n return $this->getNewInstance()->crm()->objects()->basicApi()\n ->getById('meeting', $engagementId, null, 'contact,company,deal');\n }\n\n public function deleteEngagement(string $engagementId): void\n {\n $this->getInstance()->engagements()->delete((int) $engagementId);\n }\n\n public function getAssociationsData(array $ids, string $fromObject, string $toObject): array\n {\n $associationData = [];\n $idChunks = array_chunk($ids, self::ASSOCIATIONS_BATCH_SIZE_LIMIT);\n\n foreach ($idChunks as $idChunk) {\n try {\n $batchInput = new \\HubSpot\\Client\\Crm\\Associations\\Model\\BatchInputPublicObjectId();\n $batchInput->setInputs(array_map(function ($id) {\n $publicObjectId = new \\HubSpot\\Client\\Crm\\Associations\\Model\\PublicObjectId();\n $publicObjectId->setId($id);\n\n return $publicObjectId;\n }, $idChunk));\n\n $associatedObjectsData = $this\n ->getNewInstance()\n ->crm()\n ->associations()\n ->batchApi()\n ->read($fromObject, $toObject, $batchInput);\n\n if ($associatedObjectsData instanceof \\HubSpot\\Client\\Crm\\Associations\\Model\\BatchResponsePublicAssociationMulti) {\n foreach ($associatedObjectsData->getResults() as $association) {\n $from = $association->getFrom()->getId();\n $toAssociations = $association->getTo();\n\n if (! empty($toAssociations)) {\n $associationData[$from] = array_map(function ($item) {\n return $item->getId();\n }, $toAssociations);\n }\n }\n }\n// } catch (RateLimitException $e) {\n// throw $e;\n } catch (\\Exception $e) {\n $this->log->error('[Hubspot] Failed to fetch associations', [\n 'from_object' => $fromObject,\n 'to_object' => $toObject,\n 'reason' => $e->getMessage(),\n ]);\n }\n }\n\n return $associationData;\n }\n\n /**\n * @throws \\Exception\n */\n private function getNoteAssociationType(NoteObject $noteObject): string\n {\n return match($noteObject) {\n NoteObject::Opportunity => 'note_to_deal',\n NoteObject::Lead, NoteObject::Contact => 'note_to_contact', // or 'note_to_lead' if your portal supports it\n NoteObject::Account => 'note_to_company',\n NoteObject::Call, NoteObject::Event => throw new \\Exception('Not supported'),\n };\n }\n\n /**\n * @throws \\Exception\n */\n private function getNoteObject(NoteObject $noteObject): string\n {\n return match($noteObject) {\n NoteObject::Opportunity => 'deal',\n NoteObject::Lead, NoteObject::Contact => 'contact',\n NoteObject::Account => 'company',\n NoteObject::Call, NoteObject::Event => throw new \\Exception('Not supported'),\n };\n }\n\n public function addAssociations(string $objectType, string $associationType, array $payload): Response\n {\n $endpoint = \"/crm/v4/associations/$objectType/$associationType/batch/create\";\n\n return $this->makeRequest($endpoint, 'POST', $payload);\n }\n\n public function removeAssociations(string $objectType, string $associationType, array $payload): Response\n {\n $endpoint = \"/crm/v4/associations/$objectType/$associationType/batch/archive\";\n\n return $this->makeRequest($endpoint, 'POST', $payload);\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Services\\Crm\\Hubspot;\n\nuse HubSpot\\Client\\Crm\\Deals\\ApiException as DealApiException;\nuse HubSpot\\Client\\Crm\\Contacts\\ApiException as ContactApiException;\nuse HubSpot\\Client\\Crm\\Companies\\ApiException as CompanyApiException;\nuse HubSpot\\Client\\Crm\\Contacts\\Model\\SimplePublicObjectWithAssociations as ContactsWithAssociations;\nuse HubSpot\\Client\\Crm\\Companies\\Model\\SimplePublicObjectWithAssociations as CompaniesWithAssociations;\nuse HubSpot\\Client\\Crm\\Deals\\Model\\SimplePublicObjectWithAssociations as DealWithAssociations;\nuse HubSpot\\Client\\Crm\\Objects\\Model\\SimplePublicObjectInput;\nuse HubSpot\\Client\\Crm\\Objects\\Model\\SimplePublicObjectWithAssociations as ObjectWithAssociations;\nuse HubSpot\\Client\\Crm\\Pipelines\\Model\\Error;\nuse HubSpot\\Client\\Crm\\Pipelines\\Model\\PipelineStage;\nuse HubSpot\\Client\\Crm\\Properties\\Model\\Property;\nuse HubSpot\\Discovery\\Discovery;\nuse Jiminny\\Exceptions\\CrmException;\nuse Jiminny\\Exceptions\\RateLimitException;\nuse Jiminny\\Exceptions\\SocialAccountTokenInvalidException;\nuse Jiminny\\Jobs\\Crm\\NoteObject;\nuse Jiminny\\Models\\Crm\\Field;\nuse Jiminny\\Services\\Crm\\BaseClient;\nuse Jiminny\\Services\\Crm\\Hubspot\\DTO\\Response\\Owner;\nuse Jiminny\\Services\\SocialAccountService;\nuse SevenShores\\Hubspot\\Exceptions\\BadRequest;\nuse SevenShores\\Hubspot\\Exceptions\\HubspotException;\nuse SevenShores\\Hubspot\\Factory;\nuse SevenShores\\Hubspot\\Http\\Response;\nuse Jiminny\\Services\\Crm\\Hubspot\\Pagination\\HubspotPaginationService;\nuse Illuminate\\Support\\Facades\\Redis;\nuse Throwable;\n\n/**\n * @phpstan-type CrmFieldOption array{id:string, label:string, value?:string}\n */\nclass Client extends BaseClient implements HubspotClientInterface\n{\n public const string MIN_API_VERSION = '2';\n\n public const string BASE_URL = 'https://api.hubapi.com';\n\n public const int ASSOCIATIONS_BATCH_SIZE_LIMIT = 1000;\n\n private HubspotPaginationService $paginationService;\n private HubspotTokenManager $tokenManager;\n\n public function __construct(\n SocialAccountService $socialAccountService,\n HubspotPaginationService $paginationService,\n HubspotTokenManager $tokenManager\n ) {\n parent::__construct($socialAccountService);\n $this->paginationService = $paginationService;\n $this->tokenManager = $tokenManager;\n\n $this->setBaseUrl(self::BASE_URL);\n $this->setVersion(self::MIN_API_VERSION);\n }\n\n /**\n * Reacts to a rate limits (429) from HubSpot by translating it\n * into a RateLimitException carrying retry_after.\n *\n * Wrap any outbound HubSpot call (SDK or raw HTTP) like:\n *\n * $this->executeRequest(fn () => $this->getNewInstance()->crm()->...);\n *\n * @template T\n * @param callable(): T $apiCall\n * @return T\n *\n * @throws RateLimitException\n */\n private function executeRequest(callable $apiCall)\n {\n $cacheKey = $this->getRateLimitCacheKey();\n\n $cachedRetryAfter = Redis::get($cacheKey);\n if (is_string($cachedRetryAfter) && is_numeric($cachedRetryAfter)) {\n throw new RateLimitException(\n 'Hubspot rate limit (cached circuit-breaker)',\n (int) $cachedRetryAfter,\n );\n }\n\n try {\n return $apiCall();\n } catch (Throwable $e) {\n if ($this->isHubspotRateLimit($e)) {\n $retryAfter = $this->parseRetryAfter($e);\n\n Redis::setex($cacheKey, $retryAfter, (string) $retryAfter);\n\n $this->log->warning('[Hubspot] Received 429 from API', [\n 'team_id' => $this->config->team_id,\n 'config_id' => $this->config->getId(),\n 'retry_after' => $retryAfter,\n 'message' => $this->parseMessage($e),\n 'reason' => $e->getMessage(),\n ]);\n\n throw new RateLimitException('Hubspot returned 429', $retryAfter, $e);\n }\n\n throw $e;\n }\n }\n\n private function getRateLimitCacheKey(): string\n {\n return sprintf('hubspot:ratelimit:portal:%d', $this->config->getId());\n }\n\n public function isHubspotRateLimit(Throwable $e): bool\n {\n if ($e instanceof BadRequest\n || $e instanceof DealApiException\n || $e instanceof ContactApiException\n || $e instanceof CompanyApiException\n || $e instanceof \\GuzzleHttp\\Exception\\RequestException\n ) {\n return (int) $e->getCode() === 429;\n }\n\n return false;\n }\n\n public function parseRetryAfter(Throwable $e): int\n {\n if (method_exists($e, 'getResponseHeaders')) {\n $headers = $e->getResponseHeaders() ?: [];\n $value = $headers['Retry-After'] ?? $headers['retry-after'] ?? null;\n if (is_array($value)) {\n $value = $value[0] ?? null;\n }\n if (is_numeric($value)) {\n return (int) $value;\n }\n }\n\n $message = strtolower($this->parseMessage($e));\n\n if (str_contains($message, 'daily')) {\n return 600;\n }\n if (str_contains($message, 'ten secondly')) {\n return 10;\n }\n if (str_contains($message, 'secondly')) {\n return 1;\n }\n\n $this->log->warning('[Hubspot] No retry-after header or known message, using default', [\n 'exception_class' => get_class($e),\n 'message' => $message,\n ]);\n\n return 10;\n }\n\n public function parseMessage(Throwable $e): string\n {\n if (! method_exists($e, 'getResponseBody')) {\n return '';\n }\n\n $body = $e->getResponseBody();\n if (is_string($body)) {\n $body = json_decode($body, true) ?? [];\n }\n\n if (! is_array($body)) {\n return '';\n }\n\n return (string) ($body['message'] ?? '');\n }\n\n public function getMinimumApiVersion(): string\n {\n return self::MIN_API_VERSION;\n }\n\n public function getInstance(): Factory\n {\n return new Factory([\n 'key' => $this->accessToken,\n 'oauth2' => true,\n 'base_url' => $this->baseUrl,\n ]);\n }\n\n public function getNewInstance(): Discovery\n {\n return \\HubSpot\\Factory::createWithAccessToken($this->accessToken);\n }\n\n /**\n * Secondly and daily limits for Hubspot API\n *\n * Product Tier: Free & Starter | Professional & Enterprise | API add-on (any tier)\n * Burst: 100/10 seconds | 150/10 seconds | 200/10 seconds\n * Daily: 250,000 | 500,000 | 1,000,000\n *\n * Official documentation states: The search endpoints are rate limited to five requests per second.\n * Since with 5 RPS were still hitting secondly rate limits we lowered it to 4\n */\n public function getPaginatedData(array $payload, string $type, int $offset = 0): array\n {\n $total = 0;\n $lastId = null;\n $rows = [];\n foreach ($this->getPaginatedDataGenerator($payload, $type, $offset, $total, $lastId) as $row) {\n $rows[] = $row;\n }\n\n return ['results' => $rows, 'total' => $total, 'last_record' => $lastId];\n }\n\n /**\n * @throws HubspotException\n * @throws SocialAccountTokenInvalidException\n * @throws BadRequest\n */\n public function getPaginatedDataGenerator(\n array $payload,\n string $type,\n int $offset = 0,\n int &$total = 0,\n ?string &$lastRecordId = null\n ): \\Generator {\n return $this->paginationService->getPaginatedDataGenerator(\n $this,\n $payload,\n $type,\n $offset,\n $total,\n $lastRecordId\n );\n }\n\n /**\n * Execute a search request against HubSpot CRM objects with rate limiting.\n *\n * @param string $objectType The object type ('deals', 'companies', 'contacts', 'calls')\n * @param array<string, mixed> $payload The search payload with filters, sorts, properties, etc.\n * @return array The search response with 'results', 'total', 'paging' keys\n * @throws RateLimitException When rate limit is hit\n * @throws HubspotException On API errors\n */\n public function search(string $objectType, array $payload): array\n {\n $endpoint = self::BASE_URL . \"/crm/v3/objects/{$objectType}/search\";\n\n return $this->executeRequest(function () use ($endpoint, $payload) {\n $response = $this->getInstance()->getClient()->request('POST', $endpoint, ['json' => $payload]);\n\n return $response->toArray();\n });\n }\n\n /**\n * @throws DealApiException\n * @throws CrmException\n */\n public function getOpportunityById(string $crmId, array $fields): array\n {\n try {\n// $deal = $this->executeRequest(fn () => $this->getNewInstance()->crm()->deals()->basicApi()->getById(\n $deal = $this->getNewInstance()->crm()->deals()->basicApi()->getById(\n $crmId,\n implode(',', $fields),\n 'companies,contacts'\n );\n } catch (DealApiException $e) {\n $this->log->info('[Hubspot] Failed to fetch opportunity', [\n 'crm_id' => $crmId,\n 'reason' => $e->getMessage(),\n ]);\n\n throw $e;\n }\n\n if (! $deal instanceof DealWithAssociations) {\n throw new CrmException('Deal not found');\n }\n\n return [\n 'id' => $deal->getId(),\n 'properties' => $deal->getProperties(),\n 'associations' => $deal->getAssociations(),\n ];\n }\n\n /**\n * Generic batch read method for HubSpot objects\n *\n * @param string $objectType The object type ('deals', 'companies', 'contacts')\n * @param array<string> $crmIds Array of HubSpot object IDs (max 100)\n * @param array<string> $fields Array of property names to fetch\n *\n * @return array<string, array> Array keyed by CRM ID with object data\n */\n private function batchReadObjects(string $objectType, array $crmIds, array $fields): array\n {\n if (empty($crmIds)) {\n return [];\n }\n\n $this->validateBatchSize($objectType, $crmIds);\n $this->ensureValidToken();\n\n try {\n $batchConfig = $this->createBatchConfiguration($objectType);\n $batchReadRequest = $this->prepareBatchRequest($batchConfig, $crmIds, $fields);\n $response = $batchConfig['api']->read($batchReadRequest);\n\n $this->validateApiResponse($response, $objectType);\n\n $results = $this->processApiResults($response);\n $this->logBatchResults($objectType, $crmIds, $results);\n\n return $results;\n } catch (\\Throwable $e) {\n $this->handleBatchError($e, $objectType, $crmIds);\n }\n }\n\n private function validateBatchSize(string $objectType, array $crmIds): void\n {\n if (count($crmIds) > 100) {\n throw new \\InvalidArgumentException(\"Batch size cannot exceed 100 {$objectType}\");\n }\n }\n\n private function createBatchConfiguration(string $objectType): array\n {\n $configurations = [\n 'deals' => [\n 'batchReadRequest' => new \\HubSpot\\Client\\Crm\\Deals\\Model\\BatchReadInputSimplePublicObjectId(),\n 'inputClass' => \\HubSpot\\Client\\Crm\\Deals\\Model\\SimplePublicObjectId::class,\n 'api' => $this->getNewInstance()->crm()->deals()->batchApi(),\n ],\n 'companies' => [\n 'batchReadRequest' => new \\HubSpot\\Client\\Crm\\Companies\\Model\\BatchReadInputSimplePublicObjectId(),\n 'inputClass' => \\HubSpot\\Client\\Crm\\Companies\\Model\\SimplePublicObjectId::class,\n 'api' => $this->getNewInstance()->crm()->companies()->batchApi(),\n ],\n 'contacts' => [\n 'batchReadRequest' => new \\HubSpot\\Client\\Crm\\Contacts\\Model\\BatchReadInputSimplePublicObjectId(),\n 'inputClass' => \\HubSpot\\Client\\Crm\\Contacts\\Model\\SimplePublicObjectId::class,\n 'api' => $this->getNewInstance()->crm()->contacts()->batchApi(),\n ],\n ];\n\n if (! isset($configurations[$objectType])) {\n throw new \\InvalidArgumentException(\"Unsupported object type: {$objectType}\");\n }\n\n return $configurations[$objectType];\n }\n\n private function prepareBatchRequest(array $batchConfig, array $crmIds, array $fields): object\n {\n $batchReadRequest = $batchConfig['batchReadRequest'];\n $inputClass = $batchConfig['inputClass'];\n\n $inputs = array_map(function ($crmId) use ($inputClass) {\n $input = new $inputClass();\n $input->setId($crmId);\n\n return $input;\n }, $crmIds);\n\n $batchReadRequest->setInputs($inputs);\n $batchReadRequest->setProperties($fields);\n\n return $batchReadRequest;\n }\n\n private function validateApiResponse($response, string $objectType): void\n {\n if (! $response) {\n throw new CrmException(\"HubSpot API returned null response for {$objectType} batch read\");\n }\n }\n\n private function processApiResults($response): array\n {\n $results = [];\n $responseResults = $response->getResults();\n\n if ($responseResults) {\n foreach ($responseResults as $object) {\n if ($object && $object->getId()) {\n $results[$object->getId()] = [\n 'id' => $object->getId(),\n 'properties' => $object->getProperties() ?: [],\n ];\n }\n }\n }\n\n return $results;\n }\n\n private function logBatchResults(string $objectType, array $crmIds, array $results): void\n {\n $this->log->info(\"[HubSpot] Batch fetched {$objectType}\", [\n 'requested_count' => count($crmIds),\n 'returned_count' => count($results),\n 'crm_ids' => $crmIds,\n ]);\n }\n\n private function handleBatchError(\\Throwable $e, string $objectType, array $crmIds): void\n {\n $errorMessage = $e->getMessage() ?: 'Unknown error';\n $errorTrace = $e->getTraceAsString() ?: 'No trace available';\n\n $this->log->error(\"[HubSpot] Failed to batch fetch {$objectType}\", [\n 'crm_ids' => $crmIds,\n 'error' => $errorMessage,\n 'trace' => $errorTrace,\n ]);\n\n throw new CrmException(\"Failed to batch fetch {$objectType}: \" . $errorMessage);\n }\n\n /**\n * Batch read multiple opportunities by their CRM IDs\n *\n * @param array<string> $crmIds Array of HubSpot deal IDs (max 100)\n * @param array<string> $fields Array of property names to fetch\n *\n * @return array<string, array> Array keyed by CRM ID with opportunity data\n */\n public function getOpportunitiesByIds(array $crmIds, array $fields): array\n {\n return $this->batchReadObjects('deals', $crmIds, $fields);\n }\n\n /**\n * Batch read multiple companies by their CRM IDs\n *\n * @param array<string> $crmIds Array of HubSpot company IDs (max 100)\n * @param array<string> $fields Array of property names to fetch\n *\n * @return array<string, array> Array keyed by CRM ID with company data\n */\n public function getCompaniesByIds(array $crmIds, array $fields): array\n {\n return $this->batchReadObjects('companies', $crmIds, $fields);\n }\n\n /**\n * Batch read multiple contacts by their CRM IDs\n *\n * @param array<string> $crmIds Array of HubSpot contact IDs (max 100)\n * @param array<string> $fields Array of property names to fetch\n *\n * @return array<string, array> Array keyed by CRM ID with contact data\n */\n public function getContactsByIds(array $crmIds, array $fields): array\n {\n return $this->batchReadObjects('contacts', $crmIds, $fields);\n }\n\n /**\n * @throws CompanyApiException\n * @throws CrmException\n */\n public function getAccountById(string $crmId, array $fields): array\n {\n try {\n $company = $this->getNewInstance()->crm()->companies()->basicApi()->getById(\n $crmId,\n implode(',', $fields),\n );\n } catch (CompanyApiException $e) {\n $this->log->info('[Hubspot] Failed to fetch account', [\n 'crm_id' => $crmId,\n 'reason' => $e->getMessage(),\n ]);\n\n throw $e;\n }\n\n if (! $company instanceof CompaniesWithAssociations) {\n throw new CrmException('Account not found');\n }\n\n return [\n 'id' => $company->getId(),\n 'properties' => $company->getProperties(),\n ];\n }\n\n /**\n * @throws ContactApiException\n * @throws CrmException\n */\n public function getContactById(string $crmId, array $fields): array\n {\n try {\n $contact = $this->getNewInstance()->crm()->contacts()->basicApi()->getById(\n $crmId,\n implode(',', $fields)\n );\n } catch (ContactApiException $e) {\n $this->log->info('[Hubspot] Failed to fetch contact', [\n 'crm_id' => $crmId,\n 'reason' => $e->getMessage(),\n ]);\n\n throw $e;\n }\n\n if (! $contact instanceof ContactsWithAssociations) {\n throw new CrmException('Contact not found');\n }\n\n return [\n 'id' => $contact->getId(),\n 'properties' => $contact->getProperties(),\n ];\n }\n\n /**\n * This is email search request that Hubspot offers as GET (more generous quota)\n */\n public function getContactByEmail(string $email, array $fields = []): array\n {\n try {\n $contact = $this->getNewInstance()->crm()->contacts()->basicApi()->getById(\n $email,\n implode(',', $fields),\n null,\n false,\n 'email'\n );\n\n return [\n 'id' => $contact->getId(),\n 'properties' => $contact->getProperties(),\n ];\n } catch (ContactApiException $e) {\n $this->log->info('[Hubspot] Failed to fetch contact', [\n 'email' => $email,\n 'reason' => $e->getMessage(),\n ]);\n\n return [];\n }\n }\n\n /**\n * @throws CrmException\n */\n public function fetchProperty(string $objectType, string $propertyId): Property\n {\n $result = $this->getNewInstance()->crm()->properties()->coreApi()->getByName($objectType, $propertyId);\n\n if (! $result instanceof Property) {\n $this->log->error('[Hubspot] Failed to fetch property', [\n 'object_type' => $objectType,\n 'property_id' => $propertyId,\n 'reason' => $result->getMessage(),\n ]);\n\n throw new CrmException('Failed to fetch property');\n }\n\n return $result;\n }\n\n /**\n * @return array<CrmFieldOption>\n */\n public function fetchPropertyOptions(string $objectType, string $propertyId): array\n {\n /** @var array<CrmFieldOption> */\n return $this->fetchProperty($objectType, $propertyId)->getOptions();\n }\n\n /**\n * @return array<array{id:string, label:string, deleted:bool}>\n */\n public function fetchCallDispositions(): array\n {\n /** @var Response $response */\n $response = $this->getInstance()->engagements()->getCallDispositions();\n\n /**\n * @var array<array{\n * id:string,\n * label:string,\n * deleted: bool\n * }>\n */\n return $response->toArray();\n }\n\n /**\n * @return array<CrmFieldOption>\n */\n public function fetchOpportunityPipelineStages(): array\n {\n $stages = [];\n $apiResponse = $this->getNewInstance()->crm()->pipelines()->pipelinesApi()->getAll('deals');\n\n if ($apiResponse instanceof Error) {\n $this->log->error('[Hubspot] Failed to fetch opportunity pipelines', [\n 'reason' => $apiResponse->getMessage(),\n ]);\n\n return [];\n }\n\n foreach ($apiResponse->getResults() as $pipeline) {\n $pipelineStages = array_map(\n static function (PipelineStage $stage) {\n return [\n 'id' => $stage->getId(),\n 'label' => $stage->getLabel(),\n ];\n },\n $pipeline->getStages()\n );\n\n $stages = array_merge($stages, $pipelineStages);\n }\n\n return $stages;\n }\n\n public function fetchOpportunityPipelines(): array\n {\n $pipelines = [];\n\n try {\n $apiResponse = $this->makeRequest('/crm/v3/pipelines/deals');\n } catch (\\Exception $e) {\n $this->log->info('[Hubspot] Failed to fetch opportunity pipelines', [\n 'reason' => $e->getMessage(),\n ]);\n\n return [];\n }\n\n $response = $apiResponse->toArray();\n\n foreach ($response['results'] as $pipeline) {\n $pipelines[] = [\n 'id' => $pipeline['id'],\n 'label' => $pipeline['label'],\n ];\n }\n\n return $pipelines;\n }\n\n /**\n * @return array<CrmFieldOption>\n */\n public function fetchMeetingOutcomeFieldOptions(Field $field): array\n {\n return $field->getCrmProviderId() === 'meetingOutcome'\n ? $this->fetchMeetingOutcomeTypes()\n : $this->fetchCallActivityTypes();\n }\n\n public function fetchMeetingOutcomeTypes(): array\n {\n return $this->extractMeetingTypeOptions(\n 'https://api.hubapi.com/crm/v3/properties/meeting/hs_meeting_outcome'\n );\n }\n\n public function fetchCallActivityTypes(): array\n {\n return $this->extractMeetingTypeOptions(\n 'https://api.hubapi.com/crm/v3/properties/call/hs_activity_type'\n );\n }\n\n private function extractMeetingTypeOptions(string $endpoint): array\n {\n /** @var Response $response */\n $response = $this->getInstance()\n ->getClient()\n ->request('GET', $endpoint);\n\n /**\n * @var array<array{\n * value: string,\n * label: string,\n * displayOrder: int\n * }> $optionData\n */\n $optionData = $response->toArray()['options'] ?? [];\n\n $options = [];\n foreach ($optionData as $item) {\n $options[] = [\n 'id' => $item['value'],\n 'value' => $item['value'],\n 'label' => $item['label'],\n 'display_order' => $item['displayOrder'],\n ];\n }\n\n return $options;\n }\n\n /**\n * @return array<CrmFieldOption>\n */\n public function fetchDispositionFieldOptions(): array\n {\n $options = [];\n\n $dispositions = $this->fetchCallDispositions();\n\n foreach ($dispositions as $disposition) {\n if ($disposition['deleted'] !== false) {\n continue;\n }\n\n $option['value'] = $disposition['id'];\n $option['id'] = $disposition['id'];\n $option['label'] = $disposition['label'];\n\n $options[] = $option;\n }\n\n return $options;\n }\n\n /**\n * @return array<CrmFieldOption>\n */\n public function fetchOpportunityFieldOptions(Field $field): array\n {\n if ($field->isStageField()) {\n return $this->fetchOpportunityPipelineStages();\n }\n\n if ($field->isPipelineField()) {\n return $this->fetchOpportunityPipelines();\n }\n\n return $this->fetchPropertyOptions('deals', $field->getCrmProviderId());\n }\n\n /**\n * @throws BadRequest\n * @throws HubspotException\n */\n public function makeRequest(string $endpoint, $method = 'GET', $payload = [], ?string $queryString = null)\n {\n $endpoint = self::BASE_URL . $endpoint;\n\n if ($method === 'GET') {\n return $this->getInstance()->getClient()?->request(\n method: $method,\n endpoint: $endpoint,\n query_string: $queryString\n );\n } else {\n return $this->getInstance()->getClient()->request($method, $endpoint, [\n 'json' => ($payload),\n ]);\n }\n }\n\n /**\n * @throws BadRequest\n * @throws HubspotException\n */\n public function createMeeting(array $payload): Response\n {\n $endpoint = '/crm/v3/objects/meetings';\n\n return $this->makeRequest($endpoint, 'POST', $payload);\n }\n\n /**\n * @throws BadRequest\n * @throws HubspotException\n */\n public function updateMeeting(string $meetingId, array $payload): Response\n {\n $endpoint = '/crm/v3/objects/meetings/' . $meetingId;\n\n return $this->makeRequest($endpoint, 'PATCH', $payload);\n }\n\n /**\n * @throws \\Exception\n */\n public function createNote(\n string $body,\n string $ownerId,\n int $timestamp,\n string $objectId,\n NoteObject $noteObject\n ): ?string {\n try {\n $noteInput = new SimplePublicObjectInput([\n 'properties' => [\n 'hs_note_body' => $body,\n 'hubspot_owner_id' => $ownerId,\n 'hs_timestamp' => $timestamp,\n ],\n ]);\n\n // Create note\n $note = $this->getNewInstance()->crm()->objects()->basicApi()->create('note', $noteInput);\n\n $this->getNewInstance()->crm()->objects()->associationsApi()->create(\n 'note',\n $note->getId(),\n $this->getNoteObject($noteObject),\n $objectId,\n $this->getNoteAssociationType($noteObject),\n );\n\n return $note->getId();\n } catch (\\Exception $e) {\n $this->log->error('[Hubspot] Failed to create note', [\n 'objectId' => $objectId,\n 'noteObject' => $noteObject->getObjectType(),\n 'reason' => $e->getMessage(),\n ]);\n\n \\Sentry::captureException($e);\n }\n\n return null;\n }\n\n public function updateEngagement(string $objectId, array $engagement, array $metadata): void\n {\n $this->getInstance()->engagements()->update($objectId, $engagement, $metadata);\n }\n\n public function getEngagementData(string $engagementId): array\n {\n $engagement = $this->getInstance()->engagements()->get($engagementId);\n\n return $engagement->toArray();\n }\n\n public function createEngagement(array $engagement, array $associations, array $metadata): Response\n {\n return $this->getInstance()\n ->engagements()\n ->create($engagement, $associations, $metadata);\n }\n\n public function isUnauthorizedException(\\Exception $e): bool\n {\n // Check for specific HubSpot API exception types first\n if ($e instanceof BadRequest) {\n // BadRequest can contain 401 status codes\n return $e->getCode() === 401;\n }\n\n // Check for HTTP client exceptions with status codes\n if ($e instanceof \\GuzzleHttp\\Exception\\RequestException && $e->hasResponse()) {\n $response = $e->getResponse();\n if ($response !== null) {\n return $response->getStatusCode() === 401;\n }\n }\n\n // Check for Guzzle HTTP exceptions\n if ($e instanceof \\GuzzleHttp\\Exception\\ClientException) {\n return $e->getCode() === 401;\n }\n\n // Fallback to string matching as last resort, but be more specific\n $message = strtolower($e->getMessage());\n\n return str_contains($message, '401 unauthorized') ||\n str_contains($message, 'http 401') ||\n str_contains($message, 'status code 401') ||\n (preg_match('/\\b401\\b/', $message) && str_contains($message, 'unauthorized'));\n }\n\n /**\n * Validates and refreshes the access token if needed before API requests.\n * This ensures long-running processes don't fail due to token expiration.\n *\n * @throws SocialAccountTokenInvalidException\n */\n public function ensureValidToken(): void\n {\n if ($this->oauthAccount === null) {\n return;\n }\n\n $newToken = $this->tokenManager->ensureValidToken($this->oauthAccount);\n if ($newToken !== null) {\n $this->accessToken = $newToken;\n }\n }\n\n public function getConfig()\n {\n return $this->config;\n }\n\n // returns only active (archived=false)\n public function getOwners(): array\n {\n return $this->getNewInstance()->crm()->owners()->getAll();\n }\n\n /**\n * @param bool $archived\n *\n * @return array<Owner>|[]\n */\n public function getOwnersArchived(bool $archived = true): array\n {\n $endpoint = '/crm/v3/owners';\n $queryParams = [\n 'archived' => $archived ? 'true' : 'false',\n ];\n $queryString = http_build_query($queryParams);\n\n $owners = [];\n\n try {\n $response = $this->makeRequest(endpoint: $endpoint, queryString: $queryString);\n $responseData = $response?->toArray();\n\n foreach ($responseData['results'] as $result) {\n try {\n $owners[] = Owner::create($result);\n } catch (Throwable $e) {\n $this->log->error('[HubSpot] Failed to process owner data', [\n 'result' => $result,\n 'error' => $e->getMessage(),\n ]);\n\n continue;\n }\n }\n } catch (Throwable $e) {\n $this->log->error('HubSpot] Failed to fetch owners', [\n 'archived' => $archived,\n 'error' => $e->getMessage(),\n ]);\n\n return [];\n }\n\n return $owners;\n }\n\n public function getMeeting(string $engagementId): ObjectWithAssociations\n {\n return $this->getNewInstance()->crm()->objects()->basicApi()\n ->getById('meeting', $engagementId, null, 'contact,company,deal');\n }\n\n public function deleteEngagement(string $engagementId): void\n {\n $this->getInstance()->engagements()->delete((int) $engagementId);\n }\n\n public function getAssociationsData(array $ids, string $fromObject, string $toObject): array\n {\n $associationData = [];\n $idChunks = array_chunk($ids, self::ASSOCIATIONS_BATCH_SIZE_LIMIT);\n\n foreach ($idChunks as $idChunk) {\n try {\n $batchInput = new \\HubSpot\\Client\\Crm\\Associations\\Model\\BatchInputPublicObjectId();\n $batchInput->setInputs(array_map(function ($id) {\n $publicObjectId = new \\HubSpot\\Client\\Crm\\Associations\\Model\\PublicObjectId();\n $publicObjectId->setId($id);\n\n return $publicObjectId;\n }, $idChunk));\n\n $associatedObjectsData = $this\n ->getNewInstance()\n ->crm()\n ->associations()\n ->batchApi()\n ->read($fromObject, $toObject, $batchInput);\n\n if ($associatedObjectsData instanceof \\HubSpot\\Client\\Crm\\Associations\\Model\\BatchResponsePublicAssociationMulti) {\n foreach ($associatedObjectsData->getResults() as $association) {\n $from = $association->getFrom()->getId();\n $toAssociations = $association->getTo();\n\n if (! empty($toAssociations)) {\n $associationData[$from] = array_map(function ($item) {\n return $item->getId();\n }, $toAssociations);\n }\n }\n }\n// } catch (RateLimitException $e) {\n// throw $e;\n } catch (\\Exception $e) {\n $this->log->error('[Hubspot] Failed to fetch associations', [\n 'from_object' => $fromObject,\n 'to_object' => $toObject,\n 'reason' => $e->getMessage(),\n ]);\n }\n }\n\n return $associationData;\n }\n\n /**\n * @throws \\Exception\n */\n private function getNoteAssociationType(NoteObject $noteObject): string\n {\n return match($noteObject) {\n NoteObject::Opportunity => 'note_to_deal',\n NoteObject::Lead, NoteObject::Contact => 'note_to_contact', // or 'note_to_lead' if your portal supports it\n NoteObject::Account => 'note_to_company',\n NoteObject::Call, NoteObject::Event => throw new \\Exception('Not supported'),\n };\n }\n\n /**\n * @throws \\Exception\n */\n private function getNoteObject(NoteObject $noteObject): string\n {\n return match($noteObject) {\n NoteObject::Opportunity => 'deal',\n NoteObject::Lead, NoteObject::Contact => 'contact',\n NoteObject::Account => 'company',\n NoteObject::Call, NoteObject::Event => throw new \\Exception('Not supported'),\n };\n }\n\n public function addAssociations(string $objectType, string $associationType, array $payload): Response\n {\n $endpoint = \"/crm/v4/associations/$objectType/$associationType/batch/create\";\n\n return $this->makeRequest($endpoint, 'POST', $payload);\n }\n\n public function removeAssociations(string $objectType, string $associationType, array $payload): Response\n {\n $endpoint = \"/crm/v4/associations/$objectType/$associationType/batch/archive\";\n\n return $this->makeRequest($endpoint, 'POST', $payload);\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":true,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"19","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"[2026-05-07 14:21:15] local.INFO: [Hubspot] DEBUG Getting headers {\n\"headers\":{\n\"Date\":[\"Thu,07 May 2026 14:21:15 GMT\"],\n \"Content-Type\":[\"application/json;charset=utf-8\"],\n \"Transfer-Encoding\":[\"chunked\"],\n \"Connection\":[\"keep-alive\"],\n \"CF-Ray\":[\"9f80deb8db60dc3a-SOF\"],\n \"CF-Cache-Status\":[\"DYNAMIC\"],\n \"Strict-Transport-Security\":[\"max-age=31536000; includeSubDomains; preload\"],\n \"Vary\":[\"origin,\n accept-encoding\"],\n \"access-control-allow-credentials\":[\"false\"],\n \"server-timing\":[\"hcid;desc=\\\"019e02d0-6fd8-7812-bdba-885b7ccb3ee3\\\",\n cfr;desc=\\\"9f80deb8e7c6dc3a-IAD\\\"\"],\n \"x-content-type-options\":[\"nosniff\"],\n \"x-hubspot-correlation-id\":[\"019e02d0-6fd8-7812-bdba-885b7ccb3ee3\"],\n \"Set-Cookie\":[\"__cf_bm=SIUrtdQgXVrik50pdqF6hZVYKhzTnQBidvMabeCtm0Y-1778163675-1.0.1.1-rI.ZggtDKxTge5zr8_2gbBfWMQQ.ufZEXDZyHz2mBUFdzdo2gTHEsOkXMSEShjK0hGYxNhUGM1ZoBpX7BcFZcHEjA7Cs_.SMUhUnd2nYjko; path=/; expires=Thu,\n 07-May-26 14:51:15 GMT; domain=.hubapi.com; HttpOnly; Secure; SameSite=None\"],\n \"Report-To\":[\"{\n\\\"endpoints\\\":[{\n\\\"url\\\":\\\"https:\\\\/\\\\/a.nel.cloudflare.com\\\\/report\\\\/v4?s=NYAlsVTP0fYm32qrSDjxYE4sd2RWRqiSp3wHsmdEgZlzoYdxI%2BIxVpHmsKn3O%2BKVA3mFIJ2m7YRECDGSM%2BW2IYTzo6FM4%2BdUIjURO8srzKSvJgZ%2BQ6R79arKQw3uHLlX\\\"}],\n\\\"group\\\":\\\"cf-nel\\\",\n\\\"max_age\\\":604800}\"],\n\"NEL\":[\"{\n\\\"success_fraction\\\":0.01,\n\\\"report_to\\\":\\\"cf-nel\\\",\n\\\"max_age\\\":604800}\"],\n\"Server\":[\"cloudflare\"]}} {\n\"correlation_id\":\"95236535-ec98-4541-b92a-adfa73b69eab\",\n\"trace_id\":\"c7ab8365-903f-46d4-9403-0e5b551e3545\"}","depth":4,"on_screen":true,"value":"[2026-05-07 14:21:15] local.INFO: [Hubspot] DEBUG Getting headers {\n\"headers\":{\n\"Date\":[\"Thu,07 May 2026 14:21:15 GMT\"],\n \"Content-Type\":[\"application/json;charset=utf-8\"],\n \"Transfer-Encoding\":[\"chunked\"],\n \"Connection\":[\"keep-alive\"],\n \"CF-Ray\":[\"9f80deb8db60dc3a-SOF\"],\n \"CF-Cache-Status\":[\"DYNAMIC\"],\n \"Strict-Transport-Security\":[\"max-age=31536000; includeSubDomains; preload\"],\n \"Vary\":[\"origin,\n accept-encoding\"],\n \"access-control-allow-credentials\":[\"false\"],\n \"server-timing\":[\"hcid;desc=\\\"019e02d0-6fd8-7812-bdba-885b7ccb3ee3\\\",\n cfr;desc=\\\"9f80deb8e7c6dc3a-IAD\\\"\"],\n \"x-content-type-options\":[\"nosniff\"],\n \"x-hubspot-correlation-id\":[\"019e02d0-6fd8-7812-bdba-885b7ccb3ee3\"],\n \"Set-Cookie\":[\"__cf_bm=SIUrtdQgXVrik50pdqF6hZVYKhzTnQBidvMabeCtm0Y-1778163675-1.0.1.1-rI.ZggtDKxTge5zr8_2gbBfWMQQ.ufZEXDZyHz2mBUFdzdo2gTHEsOkXMSEShjK0hGYxNhUGM1ZoBpX7BcFZcHEjA7Cs_.SMUhUnd2nYjko; path=/; expires=Thu,\n 07-May-26 14:51:15 GMT; domain=.hubapi.com; HttpOnly; Secure; SameSite=None\"],\n \"Report-To\":[\"{\n\\\"endpoints\\\":[{\n\\\"url\\\":\\\"https:\\\\/\\\\/a.nel.cloudflare.com\\\\/report\\\\/v4?s=NYAlsVTP0fYm32qrSDjxYE4sd2RWRqiSp3wHsmdEgZlzoYdxI%2BIxVpHmsKn3O%2BKVA3mFIJ2m7YRECDGSM%2BW2IYTzo6FM4%2BdUIjURO8srzKSvJgZ%2BQ6R79arKQw3uHLlX\\\"}],\n\\\"group\\\":\\\"cf-nel\\\",\n\\\"max_age\\\":604800}\"],\n\"NEL\":[\"{\n\\\"success_fraction\\\":0.01,\n\\\"report_to\\\":\\\"cf-nel\\\",\n\\\"max_age\\\":604800}\"],\n\"Server\":[\"cloudflare\"]}} {\n\"correlation_id\":\"95236535-ec98-4541-b92a-adfa73b69eab\",\n\"trace_id\":\"c7ab8365-903f-46d4-9403-0e5b551e3545\"}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
3926301823706041728
|
6378757150172121188
|
click
|
accessibility
|
NULL
|
Project: faVsco.js, menu
JY-20725-handle-HS-search Project: faVsco.js, menu
JY-20725-handle-HS-search-rate-limit, menu
Start Listening for PHP Debug Connections
HandleHubspotRateLimitTest
Run 'HandleHubspotRateLimitTest'
Debug 'HandleHubspotRateLimitTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
2
67
3
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Services\Crm\Hubspot;
use HubSpot\Client\Crm\Deals\ApiException as DealApiException;
use HubSpot\Client\Crm\Contacts\ApiException as ContactApiException;
use HubSpot\Client\Crm\Companies\ApiException as CompanyApiException;
use HubSpot\Client\Crm\Contacts\Model\SimplePublicObjectWithAssociations as ContactsWithAssociations;
use HubSpot\Client\Crm\Companies\Model\SimplePublicObjectWithAssociations as CompaniesWithAssociations;
use HubSpot\Client\Crm\Deals\Model\SimplePublicObjectWithAssociations as DealWithAssociations;
use HubSpot\Client\Crm\Objects\Model\SimplePublicObjectInput;
use HubSpot\Client\Crm\Objects\Model\SimplePublicObjectWithAssociations as ObjectWithAssociations;
use HubSpot\Client\Crm\Pipelines\Model\Error;
use HubSpot\Client\Crm\Pipelines\Model\PipelineStage;
use HubSpot\Client\Crm\Properties\Model\Property;
use HubSpot\Discovery\Discovery;
use Jiminny\Exceptions\CrmException;
use Jiminny\Exceptions\RateLimitException;
use Jiminny\Exceptions\SocialAccountTokenInvalidException;
use Jiminny\Jobs\Crm\NoteObject;
use Jiminny\Models\Crm\Field;
use Jiminny\Services\Crm\BaseClient;
use Jiminny\Services\Crm\Hubspot\DTO\Response\Owner;
use Jiminny\Services\SocialAccountService;
use SevenShores\Hubspot\Exceptions\BadRequest;
use SevenShores\Hubspot\Exceptions\HubspotException;
use SevenShores\Hubspot\Factory;
use SevenShores\Hubspot\Http\Response;
use Jiminny\Services\Crm\Hubspot\Pagination\HubspotPaginationService;
use Illuminate\Support\Facades\Redis;
use Throwable;
/**
* @phpstan-type CrmFieldOption array{id:string, label:string, value?:string}
*/
class Client extends BaseClient implements HubspotClientInterface
{
public const string MIN_API_VERSION = '2';
public const string BASE_URL = '[URL_WITH_CREDENTIALS] T
* @param callable(): T $apiCall
* @return T
*
* @throws RateLimitException
*/
private function executeRequest(callable $apiCall)
{
$cacheKey = $this->getRateLimitCacheKey();
$cachedRetryAfter = Redis::get($cacheKey);
if (is_string($cachedRetryAfter) && is_numeric($cachedRetryAfter)) {
throw new RateLimitException(
'Hubspot rate limit (cached circuit-breaker)',
(int) $cachedRetryAfter,
);
}
try {
return $apiCall();
} catch (Throwable $e) {
if ($this->isHubspotRateLimit($e)) {
$retryAfter = $this->parseRetryAfter($e);
Redis::setex($cacheKey, $retryAfter, (string) $retryAfter);
$this->log->warning('[Hubspot] Received 429 from API', [
'team_id' => $this->config->team_id,
'config_id' => $this->config->getId(),
'retry_after' => $retryAfter,
'message' => $this->parseMessage($e),
'reason' => $e->getMessage(),
]);
throw new RateLimitException('Hubspot returned 429', $retryAfter, $e);
}
throw $e;
}
}
private function getRateLimitCacheKey(): string
{
return sprintf('hubspot:ratelimit:portal:%d', $this->config->getId());
}
public function isHubspotRateLimit(Throwable $e): bool
{
if ($e instanceof BadRequest
|| $e instanceof DealApiException
|| $e instanceof ContactApiException
|| $e instanceof CompanyApiException
|| $e instanceof \GuzzleHttp\Exception\RequestException
) {
return (int) $e->getCode() === 429;
}
return false;
}
public function parseRetryAfter(Throwable $e): int
{
if (method_exists($e, 'getResponseHeaders')) {
$headers = $e->getResponseHeaders() ?: [];
$value = $headers['Retry-After'] ?? $headers['retry-after'] ?? null;
if (is_array($value)) {
$value = $value[0] ?? null;
}
if (is_numeric($value)) {
return (int) $value;
}
}
$message = strtolower($this->parseMessage($e));
if (str_contains($message, 'daily')) {
return 600;
}
if (str_contains($message, 'ten secondly')) {
return 10;
}
if (str_contains($message, 'secondly')) {
return 1;
}
$this->log->warning('[Hubspot] No retry-after header or known message, using default', [
'exception_class' => get_class($e),
'message' => $message,
]);
return 10;
}
public function parseMessage(Throwable $e): string
{
if (! method_exists($e, 'getResponseBody')) {
return '';
}
$body = $e->getResponseBody();
if (is_string($body)) {
$body = json_decode($body, true) ?? [];
}
if (! is_array($body)) {
return '';
}
return (string) ($body['message'] ?? '');
}
public function getMinimumApiVersion(): string
{
return self::MIN_API_VERSION;
}
public function getInstance(): Factory
{
return new Factory([
'key' => $this->accessToken,
'oauth2' => true,
'base_url' => $this->baseUrl,
]);
}
public function getNewInstance(): Discovery
{
return \HubSpot\Factory::createWithAccessToken($this->accessToken);
}
/**
* Secondly and daily limits for Hubspot API
*
* Product Tier: Free & Starter | Professional & Enterprise | API add-on (any tier)
* Burst: 100/10 seconds | 150/10 seconds | 200/10 seconds
* Daily: 250,000 | 500,000 | 1,000,000
*
* Official documentation states: The search endpoints are rate limited to five requests per second.
* Since with 5 RPS were still hitting secondly rate limits we lowered it to 4
*/
public function getPaginatedData(array $payload, string $type, int $offset = 0): array
{
$total = 0;
$lastId = null;
$rows = [];
foreach ($this->getPaginatedDataGenerator($payload, $type, $offset, $total, $lastId) as $row) {
$rows[] = $row;
}
return ['results' => $rows, 'total' => $total, 'last_record' => $lastId];
}
/**
* @throws HubspotException
* @throws SocialAccountTokenInvalidException
* @throws BadRequest
*/
public function getPaginatedDataGenerator(
array $payload,
string $type,
int $offset = 0,
int &$total = 0,
?string &$lastRecordId = null
): \Generator {
return $this->paginationService->getPaginatedDataGenerator(
$this,
$payload,
$type,
$offset,
$total,
$lastRecordId
);
}
/**
* Execute a search request against HubSpot CRM objects with rate limiting.
*
* @param string $objectType The object type ('deals', 'companies', 'contacts', 'calls')
* @param array<string, mixed> $payload The search payload with filters, sorts, properties, etc.
* @return array The search response with 'results', 'total', 'paging' keys
* @throws RateLimitException When rate limit is hit
* @throws HubspotException On API errors
*/
public function search(string $objectType, array $payload): array
{
$endpoint = self::BASE_URL . "/crm/v3/objects/{$objectType}/search";
return $this->executeRequest(function () use ($endpoint, $payload) {
$response = $this->getInstance()->getClient()->request('POST', $endpoint, ['json' => $payload]);
return $response->toArray();
});
}
/**
* @throws DealApiException
* @throws CrmException
*/
public function getOpportunityById(string $crmId, array $fields): array
{
try {
// $deal = $this->executeRequest(fn () => $this->getNewInstance()->crm()->deals()->basicApi()->getById(
$deal = $this->getNewInstance()->crm()->deals()->basicApi()->getById(
$crmId,
implode(',', $fields),
'companies,contacts'
);
} catch (DealApiException $e) {
$this->log->info('[Hubspot] Failed to fetch opportunity', [
'crm_id' => $crmId,
'reason' => $e->getMessage(),
]);
throw $e;
}
if (! $deal instanceof DealWithAssociations) {
throw new CrmException('Deal not found');
}
return [
'id' => $deal->getId(),
'properties' => $deal->getProperties(),
'associations' => $deal->getAssociations(),
];
}
/**
* Generic batch read method for HubSpot objects
*
* @param string $objectType The object type ('deals', 'companies', 'contacts')
* @param array<string> $crmIds Array of HubSpot object IDs (max 100)
* @param array<string> $fields Array of property names to fetch
*
* @return array<string, array> Array keyed by CRM ID with object data
*/
private function batchReadObjects(string $objectType, array $crmIds, array $fields): array
{
if (empty($crmIds)) {
return [];
}
$this->validateBatchSize($objectType, $crmIds);
$this->ensureValidToken();
try {
$batchConfig = $this->createBatchConfiguration($objectType);
$batchReadRequest = $this->prepareBatchRequest($batchConfig, $crmIds, $fields);
$response = $batchConfig['api']->read($batchReadRequest);
$this->validateApiResponse($response, $objectType);
$results = $this->processApiResults($response);
$this->logBatchResults($objectType, $crmIds, $results);
return $results;
} catch (\Throwable $e) {
$this->handleBatchError($e, $objectType, $crmIds);
}
}
private function validateBatchSize(string $objectType, array $crmIds): void
{
if (count($crmIds) > 100) {
throw new \InvalidArgumentException("Batch size cannot exceed 100 {$objectType}");
}
}
private function createBatchConfiguration(string $objectType): array
{
$configurations = [
'deals' => [
'batchReadRequest' => new \HubSpot\Client\Crm\Deals\Model\BatchReadInputSimplePublicObjectId(),
'inputClass' => \HubSpot\Client\Crm\Deals\Model\SimplePublicObjectId::class,
'api' => $this->getNewInstance()->crm()->deals()->batchApi(),
],
'companies' => [
'batchReadRequest' => new \HubSpot\Client\Crm\Companies\Model\BatchReadInputSimplePublicObjectId(),
'inputClass' => \HubSpot\Client\Crm\Companies\Model\SimplePublicObjectId::class,
'api' => $this->getNewInstance()->crm()->companies()->batchApi(),
],
'contacts' => [
'batchReadRequest' => new \HubSpot\Client\Crm\Contacts\Model\BatchReadInputSimplePublicObjectId(),
'inputClass' => \HubSpot\Client\Crm\Contacts\Model\SimplePublicObjectId::class,
'api' => $this->getNewInstance()->crm()->contacts()->batchApi(),
],
];
if (! isset($configurations[$objectType])) {
throw new \InvalidArgumentException("Unsupported object type: {$objectType}");
}
return $configurations[$objectType];
}
private function prepareBatchRequest(array $batchConfig, array $crmIds, array $fields): object
{
$batchReadRequest = $batchConfig['batchReadRequest'];
$inputClass = $batchConfig['inputClass'];
$inputs = array_map(function ($crmId) use ($inputClass) {
$input = new $inputClass();
$input->setId($crmId);
return $input;
}, $crmIds);
$batchReadRequest->setInputs($inputs);
$batchReadRequest->setProperties($fields);
return $batchReadRequest;
}
private function validateApiResponse($response, string $objectType): void
{
if (! $response) {
throw new CrmException("HubSpot API returned null response for {$objectType} batch read");
}
}
private function processApiResults($response): array
{
$results = [];
$responseResults = $response->getResults();
if ($responseResults) {
foreach ($responseResults as $object) {
if ($object && $object->getId()) {
$results[$object->getId()] = [
'id' => $object->getId(),
'properties' => $object->getProperties() ?: [],
];
}
}
}
return $results;
}
private function logBatchResults(string $objectType, array $crmIds, array $results): void
{
$this->log->info("[HubSpot] Batch fetched {$objectType}", [
'requested_count' => count($crmIds),
'returned_count' => count($results),
'crm_ids' => $crmIds,
]);
}
private function handleBatchError(\Throwable $e, string $objectType, array $crmIds): void
{
$errorMessage = $e->getMessage() ?: 'Unknown error';
$errorTrace = $e->getTraceAsString() ?: 'No trace available';
$this->log->error("[HubSpot] Failed to batch fetch {$objectType}", [
'crm_ids' => $crmIds,
'error' => $errorMessage,
'trace' => $errorTrace,
]);
throw new CrmException("Failed to batch fetch {$objectType}: " . $errorMessage);
}
/**
* Batch read multiple opportunities by their CRM IDs
*
* @param array<string> $crmIds Array of HubSpot deal IDs (max 100)
* @param array<string> $fields Array of property names to fetch
*
* @return array<string, array> Array keyed by CRM ID with opportunity data
*/
public function getOpportunitiesByIds(array $crmIds, array $fields): array
{
return $this->batchReadObjects('deals', $crmIds, $fields);
}
/**
* Batch read multiple companies by their CRM IDs
*
* @param array<string> $crmIds Array of HubSpot company IDs (max 100)
* @param array<string> $fields Array of property names to fetch
*
* @return array<string, array> Array keyed by CRM ID with company data
*/
public function getCompaniesByIds(array $crmIds, array $fields): array
{
return $this->batchReadObjects('companies', $crmIds, $fields);
}
/**
* Batch read multiple contacts by their CRM IDs
*
* @param array<string> $crmIds Array of HubSpot contact IDs (max 100)
* @param array<string> $fields Array of property names to fetch
*
* @return array<string, array> Array keyed by CRM ID with contact data
*/
public function getContactsByIds(array $crmIds, array $fields): array
{
return $this->batchReadObjects('contacts', $crmIds, $fields);
}
/**
* @throws CompanyApiException
* @throws CrmException
*/
public function getAccountById(string $crmId, array $fields): array
{
try {
$company = $this->getNewInstance()->crm()->companies()->basicApi()->getById(
$crmId,
implode(',', $fields),
);
} catch (CompanyApiException $e) {
$this->log->info('[Hubspot] Failed to fetch account', [
'crm_id' => $crmId,
'reason' => $e->getMessage(),
]);
throw $e;
}
if (! $company instanceof CompaniesWithAssociations) {
throw new CrmException('Account not found');
}
return [
'id' => $company->getId(),
'properties' => $company->getProperties(),
];
}
/**
* @throws ContactApiException
* @throws CrmException
*/
public function getContactById(string $crmId, array $fields): array
{
try {
$contact = $this->getNewInstance()->crm()->contacts()->basicApi()->getById(
$crmId,
implode(',', $fields)
);
} catch (ContactApiException $e) {
$this->log->info('[Hubspot] Failed to fetch contact', [
'crm_id' => $crmId,
'reason' => $e->getMessage(),
]);
throw $e;
}
if (! $contact instanceof ContactsWithAssociations) {
throw new CrmException('Contact not found');
}
return [
'id' => $contact->getId(),
'properties' => $contact->getProperties(),
];
}
/**
* This is email search request that Hubspot offers as GET (more generous quota)
*/
public function getContactByEmail(string $email, array $fields = []): array
{
try {
$contact = $this->getNewInstance()->crm()->contacts()->basicApi()->getById(
$email,
implode(',', $fields),
null,
false,
'email'
);
return [
'id' => $contact->getId(),
'properties' => $contact->getProperties(),
];
} catch (ContactApiException $e) {
$this->log->info('[Hubspot] Failed to fetch contact', [
'email' => $email,
'reason' => $e->getMessage(),
]);
return [];
}
}
/**
* @throws CrmException
*/
public function fetchProperty(string $objectType, string $propertyId): Property
{
$result = $this->getNewInstance()->crm()->properties()->coreApi()->getByName($objectType, $propertyId);
if (! $result instanceof Property) {
$this->log->error('[Hubspot] Failed to fetch property', [
'object_type' => $objectType,
'property_id' => $propertyId,
'reason' => $result->getMessage(),
]);
throw new CrmException('Failed to fetch property');
}
return $result;
}
/**
* @return array<CrmFieldOption>
*/
public function fetchPropertyOptions(string $objectType, string $propertyId): array
{
/** @var array<CrmFieldOption> */
return $this->fetchProperty($objectType, $propertyId)->getOptions();
}
/**
* @return array<array{id:string, label:string, deleted:bool}>
*/
public function fetchCallDispositions(): array
{
/** @var Response $response */
$response = $this->getInstance()->engagements()->getCallDispositions();
/**
* @var array<array{
* id:string,
* label:string,
* deleted: bool
* }>
*/
return $response->toArray();
}
/**
* @return array<CrmFieldOption>
*/
public function fetchOpportunityPipelineStages(): array
{
$stages = [];
$apiResponse = $this->getNewInstance()->crm()->pipelines()->pipelinesApi()->getAll('deals');
if ($apiResponse instanceof Error) {
$this->log->error('[Hubspot] Failed to fetch opportunity pipelines', [
'reason' => $apiResponse->getMessage(),
]);
return [];
}
foreach ($apiResponse->getResults() as $pipeline) {
$pipelineStages = array_map(
static function (PipelineStage $stage) {
return [
'id' => $stage->getId(),
'label' => $stage->getLabel(),
];
},
$pipeline->getStages()
);
$stages = array_merge($stages, $pipelineStages);
}
return $stages;
}
public function fetchOpportunityPipelines(): array
{
$pipelines = [];
try {
$apiResponse = $this->makeRequest('/crm/v3/pipelines/deals');
} catch (\Exception $e) {
$this->log->info('[Hubspot] Failed to fetch opportunity pipelines', [
'reason' => $e->getMessage(),
]);
return [];
}
$response = $apiResponse->toArray();
foreach ($response['results'] as $pipeline) {
$pipelines[] = [
'id' => $pipeline['id'],
'label' => $pipeline['label'],
];
}
return $pipelines;
}
/**
* @return array<CrmFieldOption>
*/
public function fetchMeetingOutcomeFieldOptions(Field $field): array
{
return $field->getCrmProviderId() === 'meetingOutcome'
? $this->fetchMeetingOutcomeTypes()
: $this->fetchCallActivityTypes();
}
public function fetchMeetingOutcomeTypes(): array
{
return $this->extractMeetingTypeOptions(
'[URL_WITH_CREDENTIALS] Response $response */
$response = $this->getInstance()
->getClient()
->request('GET', $endpoint);
/**
* @var array<array{
* value: string,
* label: string,
* displayOrder: int
* }> $optionData
*/
$optionData = $response->toArray()['options'] ?? [];
$options = [];
foreach ($optionData as $item) {
$options[] = [
'id' => $item['value'],
'value' => $item['value'],
'label' => $item['label'],
'display_order' => $item['displayOrder'],
];
}
return $options;
}
/**
* @return array<CrmFieldOption>
*/
public function fetchDispositionFieldOptions(): array
{
$options = [];
$dispositions = $this->fetchCallDispositions();
foreach ($dispositions as $disposition) {
if ($disposition['deleted'] !== false) {
continue;
}
$option['value'] = $disposition['id'];
$option['id'] = $disposition['id'];
$option['label'] = $disposition['label'];
$options[] = $option;
}
return $options;
}
/**
* @return array<CrmFieldOption>
*/
public function fetchOpportunityFieldOptions(Field $field): array
{
if ($field->isStageField()) {
return $this->fetchOpportunityPipelineStages();
}
if ($field->isPipelineField()) {
return $this->fetchOpportunityPipelines();
}
return $this->fetchPropertyOptions('deals', $field->getCrmProviderId());
}
/**
* @throws BadRequest
* @throws HubspotException
*/
public function makeRequest(string $endpoint, $method = 'GET', $payload = [], ?string $queryString = null)
{
$endpoint = self::BASE_URL . $endpoint;
if ($method === 'GET') {
return $this->getInstance()->getClient()?->request(
method: $method,
endpoint: $endpoint,
query_string: $queryString
);
} else {
return $this->getInstance()->getClient()->request($method, $endpoint, [
'json' => ($payload),
]);
}
}
/**
* @throws BadRequest
* @throws HubspotException
*/
public function createMeeting(array $payload): Response
{
$endpoint = '/crm/v3/objects/meetings';
return $this->makeRequest($endpoint, 'POST', $payload);
}
/**
* @throws BadRequest
* @throws HubspotException
*/
public function updateMeeting(string $meetingId, array $payload): Response
{
$endpoint = '/crm/v3/objects/meetings/' . $meetingId;
return $this->makeRequest($endpoint, 'PATCH', $payload);
}
/**
* @throws \Exception
*/
public function createNote(
string $body,
string $ownerId,
int $timestamp,
string $objectId,
NoteObject $noteObject
): ?string {
try {
$noteInput = new SimplePublicObjectInput([
'properties' => [
'hs_note_body' => $body,
'hubspot_owner_id' => $ownerId,
'hs_timestamp' => $timestamp,
],
]);
// Create note
$note = $this->getNewInstance()->crm()->objects()->basicApi()->create('note', $noteInput);
$this->getNewInstance()->crm()->objects()->associationsApi()->create(
'note',
$note->getId(),
$this->getNoteObject($noteObject),
$objectId,
$this->getNoteAssociationType($noteObject),
);
return $note->getId();
} catch (\Exception $e) {
$this->log->error('[Hubspot] Failed to create note', [
'objectId' => $objectId,
'noteObject' => $noteObject->getObjectType(),
'reason' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
return null;
}
public function updateEngagement(string $objectId, array $engagement, array $metadata): void
{
$this->getInstance()->engagements()->update($objectId, $engagement, $metadata);
}
public function getEngagementData(string $engagementId): array
{
$engagement = $this->getInstance()->engagements()->get($engagementId);
return $engagement->toArray();
}
public function createEngagement(array $engagement, array $associations, array $metadata): Response
{
return $this->getInstance()
->engagements()
->create($engagement, $associations, $metadata);
}
public function isUnauthorizedException(\Exception $e): bool
{
// Check for specific HubSpot API exception types first
if ($e instanceof BadRequest) {
// BadRequest can contain 401 status codes
return $e->getCode() === 401;
}
// Check for HTTP client exceptions with status codes
if ($e instanceof \GuzzleHttp\Exception\RequestException && $e->hasResponse()) {
$response = $e->getResponse();
if ($response !== null) {
return $response->getStatusCode() === 401;
}
}
// Check for Guzzle HTTP exceptions
if ($e instanceof \GuzzleHttp\Exception\ClientException) {
return $e->getCode() === 401;
}
// Fallback to string matching as last resort, but be more specific
$message = strtolower($e->getMessage());
return str_contains($message, '401 unauthorized') ||
str_contains($message, 'http 401') ||
str_contains($message, 'status code 401') ||
(preg_match('/\b401\b/', $message) && str_contains($message, 'unauthorized'));
}
/**
* Validates and refreshes the access token if needed before API requests.
* This ensures long-running processes don't fail due to token expiration.
*
* @throws SocialAccountTokenInvalidException
*/
public function ensureValidToken(): void
{
if ($this->oauthAccount === null) {
return;
}
$newToken = $this->tokenManager->ensureValidToken($this->oauthAccount);
if ($newToken !== null) {
$this->accessToken = $newToken;
}
}
public function getConfig()
{
return $this->config;
}
// returns only active (archived=false)
public function getOwners(): array
{
return $this->getNewInstance()->crm()->owners()->getAll();
}
/**
* @param bool $archived
*
* @return array<Owner>|[]
*/
public function getOwnersArchived(bool $archived = true): array
{
$endpoint = '/crm/v3/owners';
$queryParams = [
'archived' => $archived ? 'true' : 'false',
];
$queryString = http_build_query($queryParams);
$owners = [];
try {
$response = $this->makeRequest(endpoint: $endpoint, queryString: $queryString);
$responseData = $response?->toArray();
foreach ($responseData['results'] as $result) {
try {
$owners[] = Owner::create($result);
} catch (Throwable $e) {
$this->log->error('[HubSpot] Failed to process owner data', [
'result' => $result,
'error' => $e->getMessage(),
]);
continue;
}
}
} catch (Throwable $e) {
$this->log->error('HubSpot] Failed to fetch owners', [
'archived' => $archived,
'error' => $e->getMessage(),
]);
return [];
}
return $owners;
}
public function getMeeting(string $engagementId): ObjectWithAssociations
{
return $this->getNewInstance()->crm()->objects()->basicApi()
->getById('meeting', $engagementId, null, 'contact,company,deal');
}
public function deleteEngagement(string $engagementId): void
{
$this->getInstance()->engagements()->delete((int) $engagementId);
}
public function getAssociationsData(array $ids, string $fromObject, string $toObject): array
{
$associationData = [];
$idChunks = array_chunk($ids, self::ASSOCIATIONS_BATCH_SIZE_LIMIT);
foreach ($idChunks as $idChunk) {
try {
$batchInput = new \HubSpot\Client\Crm\Associations\Model\BatchInputPublicObjectId();
$batchInput->setInputs(array_map(function ($id) {
$publicObjectId = new \HubSpot\Client\Crm\Associations\Model\PublicObjectId();
$publicObjectId->setId($id);
return $publicObjectId;
}, $idChunk));
$associatedObjectsData = $this
->getNewInstance()
->crm()
->associations()
->batchApi()
->read($fromObject, $toObject, $batchInput);
if ($associatedObjectsData instanceof \HubSpot\Client\Crm\Associations\Model\BatchResponsePublicAssociationMulti) {
foreach ($associatedObjectsData->getResults() as $association) {
$from = $association->getFrom()->getId();
$toAssociations = $association->getTo();
if (! empty($toAssociations)) {
$associationData[$from] = array_map(function ($item) {
return $item->getId();
}, $toAssociations);
}
}
}
// } catch (RateLimitException $e) {
// throw $e;
} catch (\Exception $e) {
$this->log->error('[Hubspot] Failed to fetch associations', [
'from_object' => $fromObject,
'to_object' => $toObject,
'reason' => $e->getMessage(),
]);
}
}
return $associationData;
}
/**
* @throws \Exception
*/
private function getNoteAssociationType(NoteObject $noteObject): string
{
return match($noteObject) {
NoteObject::Opportunity => 'note_to_deal',
NoteObject::Lead, NoteObject::Contact => 'note_to_contact', // or 'note_to_lead' if your portal supports it
NoteObject::Account => 'note_to_company',
NoteObject::Call, NoteObject::Event => throw new \Exception('Not supported'),
};
}
/**
* @throws \Exception
*/
private function getNoteObject(NoteObject $noteObject): string
{
return match($noteObject) {
NoteObject::Opportunity => 'deal',
NoteObject::Lead, NoteObject::Contact => 'contact',
NoteObject::Account => 'company',
NoteObject::Call, NoteObject::Event => throw new \Exception('Not supported'),
};
}
public function addAssociations(string $objectType, string $associationType, array $payload): Response
{
$endpoint = "/crm/v4/associations/$objectType/$associationType/batch/create";
return $this->makeRequest($endpoint, 'POST', $payload);
}
public function removeAssociations(string $objectType, string $associationType, array $payload): Response
{
$endpoint = "/crm/v4/associations/$objectType/$associationType/batch/archive";
return $this->makeRequest($endpoint, 'POST', $payload);
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
19
Previous Highlighted Error
Next Highlighted Error
[2026-05-07 14:21:15] local.INFO: [Hubspot] DEBUG Getting headers {
"headers":{
"Date":["Thu,07 May 2026 14:21:15 GMT"],
"Content-Type":["application/json;charset=utf-8"],
"Transfer-Encoding":["chunked"],
"Connection":["keep-alive"],
"CF-Ray":["9f80deb8db60dc3a-SOF"],
"CF-Cache-Status":["DYNAMIC"],
"Strict-Transport-Security":["max-age=31536000; includeSubDomains; preload"],
"Vary":["origin,
accept-encoding"],
"access-control-allow-credentials":["false"],
"server-timing":["hcid;desc=\"019e02d0-6fd8-7812-bdba-885b7ccb3ee3\",
cfr;desc=\"9f80deb8e7c6dc3a-IAD\""],
"x-content-type-options":["nosniff"],
"x-hubspot-correlation-id":["019e02d0-6fd8-7812-bdba-885b7ccb3ee3"],
"Set-Cookie":["__cf_bm=SIUrtdQgXVrik50pdqF6hZVYKhzTnQBidvMabeCtm0Y-1778163675-[IP_ADDRESS]-rI.ZggtDKxTge5zr8_2gbBfWMQQ.ufZEXDZyHz2mBUFdzdo2gTHEsOkXMSEShjK0hGYxNhUGM1ZoBpX7BcFZcHEjA7Cs_.SMUhUnd2nYjko; path=/; expires=Thu,
07-May-26 14:51:15 GMT; domain=.hubapi.com; HttpOnly; Secure; SameSite=None"],
"Report-To":["{
\"endpoints\":[{
\"url\":\"https:\\/\\/a.nel.cloudflare.com\\/report\\/v4?s=NYAlsVTP0fYm32qrSDjxYE4sd2RWRqiSp3wHsmdEgZlzoYdxI%2BIxVpHmsKn3O%2BKVA3mFIJ2m7YRECDGSM%2BW2IYTzo6FM4%2BdUIjURO8srzKSvJgZ%2BQ6R79arKQw3uHLlX\"}],
\"group\":\"cf-nel\",
\"max_age\":604800}"],
"NEL":["{
\"success_fraction\":0.01,
\"report_to\":\"cf-nel\",
\"max_age\":604800}"],
"Server":["cloudflare"]}} {
"correlation_id":"95236535-ec98-4541-b92a-adfa73b69eab",
"trace_id":"c7ab8365-903f-46d4-9403-0e5b551e3545"}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
18192
|
791
|
1
|
2026-05-11T11:03:39.814004+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-11/1778 /Users/lukas/.screenpipe/data/data/2026-05-11/1778497419814_m2.jpg...
|
PhpStorm
|
faVsco.js – Client.php
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
JY-20725-handle-HS-search Project: faVsco.js, menu
JY-20725-handle-HS-search-rate-limit, menu
Start Listening for PHP Debug Connections
HandleHubspotRateLimitTest
Run 'HandleHubspotRateLimitTest'
Debug 'HandleHubspotRateLimitTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
2
67
3...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"bounds":{"left":0.025930852,"top":0.019952115,"width":0.03856383,"height":0.025538707},"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JY-20725-handle-HS-search-rate-limit, menu","depth":5,"bounds":{"left":0.064494684,"top":0.019952115,"width":0.09541223,"height":0.025538707},"on_screen":true,"help_text":"Git Branch: JY-20725-handle-HS-search-rate-limit","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"bounds":{"left":0.82413566,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"HandleHubspotRateLimitTest","depth":6,"bounds":{"left":0.8394282,"top":0.019952115,"width":0.076130316,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'HandleHubspotRateLimitTest'","depth":6,"bounds":{"left":0.9155585,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'HandleHubspotRateLimitTest'","depth":6,"bounds":{"left":0.9268617,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"bounds":{"left":0.9381649,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"bounds":{"left":0.96609044,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"bounds":{"left":0.9773936,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"bounds":{"left":0.9886968,"top":0.019952115,"width":0.011303186,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"2","depth":4,"bounds":{"left":0.41156915,"top":0.19952115,"width":0.007978723,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"67","depth":4,"bounds":{"left":0.42154256,"top":0.19952115,"width":0.009973404,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"3","depth":4,"bounds":{"left":0.43351063,"top":0.19952115,"width":0.007978723,"height":0.015163607},"on_screen":true,"role_description":"text"}]...
|
7750778324264792027
|
-8740265267525727424
|
click
|
hybrid
|
NULL
|
Project: faVsco.js, menu
JY-20725-handle-HS-search Project: faVsco.js, menu
JY-20725-handle-HS-search-rate-limit, menu
Start Listening for PHP Debug Connections
HandleHubspotRateLimitTest
Run 'HandleHubspotRateLimitTest'
Debug 'HandleHubspotRateLimitTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
2
67
3
PhostormVIewINavicareCodeFV faVsco.js°9 JY-20725-handle-HS-search-rate-limiProiect© BatchSyncCollectolhuospot/service.onp•JiminnyDeougcommane.onge balchsynckealssec clientoneo closeaDealstagess)MatchactivityermData.pngDealrielasservice.gRateLimitexcedc)Decorateacuivilv.or© FieldDefinitions.phrC) CrmObiectsResolver.pho() ProviderRateLimiter.phpC) FieldT vpeconverteclass Cuient extends BasecLient imolements Hubspotcuientinterfacee Hubspotclientinterc) HubspotlokenmanC) PavloadBuilder.phpC) RemotecrmobiectrP ResponseNormalize210 Cc) Service,onoC)SvncFieldAction.onC) SvncRelatedActivitC) WebhookSvncBatclv MintearationAorM AcceccorsConfigD DTO• M SiltersD Jobs222 E• M ProcnectSearchStr.W service lraits© DataClient.php© DecorateActivity.ph(e) LocalSearch.onpu LocalSearchintertac227 C© RemoteSearch.phpc) Service.phpv W Listeners© ConvertLeadActivitc) PurceLookuocache> M Metadata> MiarationiPioedrivev Salesforce236• D FieldsM OnnortunitvMatcheMOnnortunitvSvneStl239M ProsneetSearchStr.M ServiceTraitcC) Client nhr© DecorateActivity.pr. Delete@biectsTrait© FieldDefinitions.php249© PayloadBuilder.php© Profile.php© QueryBuilder.php248* Ufficial documentation states: The search endpoints are rate limited to five requests per second.* Since with 5 RPS were still hitting secondly rate limits we lowered it to 4public function getPaginatedData(array Spayload, string $type, int $offset = 0): arrayStotal = 0;Sastid = nun•Srows = [J:foreach (Sthis->getPaginatedDataGenerator(Spayload, $type, $offset,8: Stotal.&lastRecordld: $lastId) as $row) {Srows(] = $rowreturn ['results' => $rows, 'total' => $total, 'last_record' => $lastId]:*acows nuospoccxceotlon*dchrows soc1alaccountlokenLnval1dzxcept1on* dchrows Badkequestpublic function getPaginatedDataGenerator(strina Stvoe.int Soffset = 0.int &stotal = 0.Ostrina &SlastRecordid = nuluGenerator "neturnsthis->naginationServi.ce->aetpaginatedbataGeneraton(SthisSnavload.$type,Soffset.8: Stotal..8: SlactPecordTd• Execute a search request against HubSpot CRM obiects with rate limiting.* Anaram string SobjectType The object tupe ('deals'.'contacts', 'calls')* anaram array<string, mixed> Spayload The search payload with filters, sorts, properties. etc.A6 VAY4 HS_local (iminny@localhost]# console [PKoD)A console (EU]A console [STAGING][2026-05-07 14:21:15] local.INF0: [Hubspot] DEBUG Getting headers {w19A"Vace".L"Inu,or may 20z0 14.21.10 bMl"J,"Connection":"keep-alive"]r"CF-Ray":"9t80deb8dbo0dcsa-S0F".."Strict-Transport-Security":"max-aqe=31536000* 1ncLudesubbomains: preload")."server-timing": ["hcid;desc=\"019e02d0-6fd8-7812-bdba-885b7ccb3ee3)","x-hubspot-correlation-id":["019e02d0-6fd8-7812-bdba-885b7ccb3ee3"),"Set-Cookie":["__cf_bm=S1UrtdQgXVrik50pdqF6hZVYKhzTnQBidvMabeCtm0Y-1778163675-[IP_ADDRESS]-rI.ZggtDKxTg07-May-26 14:51:15 GMT; domain=.hubapi.com; Http0nly; Secure; SameSite=None"],\"url\":"https:\\\/a.nel.cloudflare.com\\/report\\/v4?s=NYAlsVTP0fYm32qrSDjxYE4sd2RWRqiSp3wHsr\"group\":\"cf-nel\",\"max_age\":604800}"],"NEL".L"1\"success_fraction\":0.01,reporc to. "cr-nel,("max age ":604800}"]"Server": ["cloudflare"]}} {"correlation id":"95236535-ec98-4541-b92a-adfa73b69eab"."trace_1d":"C/Ab8565-905t-4604-9405-0e5b551e5545'5Preparation tor kell... In o/ m100% Lz• Mon 11 May 14:03:39HandleHubspotRateLimitTest vCascade• HubSpot Rate Limit+0 ..lets simplify it, just call it parseMessage and skip policy al togetherThought for 5s ›Read Client.php #L86-200b Client.php+17 -35Verity nothing broke:Command docker. taildocker execetests/Unit/J0bs/Middleware/HandleHubspotRatel/mitTest-phptests/Unit/Services/Crm/Hubspot/ClientTest.php --no-coverage 2>&1 | tail -10pup92.20contzaeration: Phomes3.minnv/ohpunit.xml:D.:Time: 02:14.415. Memorv: 62.00 MB[PASSWORD_DOTS]DD..DD..D.D..DDDDD.DDD...65 / 80 (808)OK hut there were icsuec!All 80 tests still passSummaryRemoved narsePo Eicv entirelv. narseRetrvAfter now mans messade kevwords direct v to seconds without theintermediate policy conceptBefore: header a narsePolicy a nolicy constant a seconds After: header « narceMessage „ secondsChanges:• parsePolicy → deleted•narseMessade added: returns the raw messade strina from the response bodv (or .. )•parseRetryAfter does the keyword matching itself (daily → 600, ten secondly → 10, secondly → 1)• Log line in executeRequest now logs message instead of policy - more informative for debuggingThe keyword-match order is the same as before (daily before ten secondly before secondly ) to avoid substringcollision.II111 1Ask anvthina (84L)« CodeClaude Onus 4.7 MediumWN Windsurf Toams 222-8 UTF.8io 4 spaces...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
18193
|
790
|
3
|
2026-05-11T11:03:41.678494+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-11/1778 /Users/lukas/.screenpipe/data/data/2026-05-11/1778497421678_m1.jpg...
|
PhpStorm
|
faVsco.js – Client.php
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
JY-20725-handle-HS-search Project: faVsco.js, menu
JY-20725-handle-HS-search-rate-limit, menu
Start Listening for PHP Debug Connections
HandleHubspotRateLimitTest
Run 'HandleHubspotRateLimitTest'
Debug 'HandleHubspotRateLimitTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
2
67
3
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Services\Crm\Hubspot;
use HubSpot\Client\Crm\Deals\ApiException as DealApiException;
use HubSpot\Client\Crm\Contacts\ApiException as ContactApiException;
use HubSpot\Client\Crm\Companies\ApiException as CompanyApiException;
use HubSpot\Client\Crm\Contacts\Model\SimplePublicObjectWithAssociations as ContactsWithAssociations;
use HubSpot\Client\Crm\Companies\Model\SimplePublicObjectWithAssociations as CompaniesWithAssociations;
use HubSpot\Client\Crm\Deals\Model\SimplePublicObjectWithAssociations as DealWithAssociations;
use HubSpot\Client\Crm\Objects\Model\SimplePublicObjectInput;
use HubSpot\Client\Crm\Objects\Model\SimplePublicObjectWithAssociations as ObjectWithAssociations;
use HubSpot\Client\Crm\Pipelines\Model\Error;
use HubSpot\Client\Crm\Pipelines\Model\PipelineStage;
use HubSpot\Client\Crm\Properties\Model\Property;
use HubSpot\Discovery\Discovery;
use Jiminny\Exceptions\CrmException;
use Jiminny\Exceptions\RateLimitException;
use Jiminny\Exceptions\SocialAccountTokenInvalidException;
use Jiminny\Jobs\Crm\NoteObject;
use Jiminny\Models\Crm\Field;
use Jiminny\Services\Crm\BaseClient;
use Jiminny\Services\Crm\Hubspot\DTO\Response\Owner;
use Jiminny\Services\SocialAccountService;
use SevenShores\Hubspot\Exceptions\BadRequest;
use SevenShores\Hubspot\Exceptions\HubspotException;
use SevenShores\Hubspot\Factory;
use SevenShores\Hubspot\Http\Response;
use Jiminny\Services\Crm\Hubspot\Pagination\HubspotPaginationService;
use Illuminate\Support\Facades\Redis;
use Throwable;
/**
* @phpstan-type CrmFieldOption array{id:string, label:string, value?:string}
*/
class Client extends BaseClient implements HubspotClientInterface
{
public const string MIN_API_VERSION = '2';
public const string BASE_URL = '[URL_WITH_CREDENTIALS] T
* @param callable(): T $apiCall
* @return T
*
* @throws RateLimitException
*/
private function executeRequest(callable $apiCall)
{
$cacheKey = $this->getRateLimitCacheKey();
$cachedRetryAfter = Redis::get($cacheKey);
if (is_string($cachedRetryAfter) && is_numeric($cachedRetryAfter)) {
throw new RateLimitException(
'Hubspot rate limit (cached circuit-breaker)',
(int) $cachedRetryAfter,
);
}
try {
return $apiCall();
} catch (Throwable $e) {
if ($this->isHubspotRateLimit($e)) {
$retryAfter = $this->parseRetryAfter($e);
Redis::setex($cacheKey, $retryAfter, (string) $retryAfter);
$this->log->warning('[Hubspot] Received 429 from API', [
'team_id' => $this->config->team_id,
'config_id' => $this->config->getId(),
'retry_after' => $retryAfter,
'message' => $this->parseMessage($e),
'reason' => $e->getMessage(),
]);
throw new RateLimitException('Hubspot returned 429', $retryAfter, $e);
}
throw $e;
}
}
private function getRateLimitCacheKey(): string
{
return sprintf('hubspot:ratelimit:portal:%d', $this->config->getId());
}
public function isHubspotRateLimit(Throwable $e): bool
{
if ($e instanceof BadRequest
|| $e instanceof DealApiException
|| $e instanceof ContactApiException
|| $e instanceof CompanyApiException
|| $e instanceof \GuzzleHttp\Exception\RequestException
) {
return (int) $e->getCode() === 429;
}
return false;
}
public function parseRetryAfter(Throwable $e): int
{
if (method_exists($e, 'getResponseHeaders')) {
$headers = $e->getResponseHeaders() ?: [];
$value = $headers['Retry-After'] ?? $headers['retry-after'] ?? null;
if (is_array($value)) {
$value = $value[0] ?? null;
}
if (is_numeric($value)) {
return (int) $value;
}
}
$message = strtolower($this->parseMessage($e));
if (str_contains($message, 'daily')) {
return 600;
}
if (str_contains($message, 'ten secondly')) {
return 10;
}
if (str_contains($message, 'secondly')) {
return 1;
}
$this->log->warning('[Hubspot] No retry-after header or known message, using default', [
'exception_class' => get_class($e),
'message' => $message,
]);
return 10;
}
public function parseMessage(Throwable $e): string
{
if (! method_exists($e, 'getResponseBody')) {
return '';
}
$body = $e->getResponseBody();
if (is_string($body)) {
$body = json_decode($body, true) ?? [];
}
if (! is_array($body)) {
return '';
}
return (string) ($body['message'] ?? '');
}
public function getMinimumApiVersion(): string
{
return self::MIN_API_VERSION;
}
public function getInstance(): Factory
{
return new Factory([
'key' => $this->accessToken,
'oauth2' => true,
'base_url' => $this->baseUrl,
]);
}
public function getNewInstance(): Discovery
{
return \HubSpot\Factory::createWithAccessToken($this->accessToken);
}
/**
* Secondly and daily limits for Hubspot API
*
* Product Tier: Free & Starter | Professional & Enterprise | API add-on (any tier)
* Burst: 100/10 seconds | 150/10 seconds | 200/10 seconds
* Daily: 250,000 | 500,000 | 1,000,000
*
* Official documentation states: The search endpoints are rate limited to five requests per second.
* Since with 5 RPS were still hitting secondly rate limits we lowered it to 4
*/
public function getPaginatedData(array $payload, string $type, int $offset = 0): array
{
$total = 0;
$lastId = null;
$rows = [];
foreach ($this->getPaginatedDataGenerator($payload, $type, $offset, $total, $lastId) as $row) {
$rows[] = $row;
}
return ['results' => $rows, 'total' => $total, 'last_record' => $lastId];
}
/**
* @throws HubspotException
* @throws SocialAccountTokenInvalidException
* @throws BadRequest
*/
public function getPaginatedDataGenerator(
array $payload,
string $type,
int $offset = 0,
int &$total = 0,
?string &$lastRecordId = null
): \Generator {
return $this->paginationService->getPaginatedDataGenerator(
$this,
$payload,
$type,
$offset,
$total,
$lastRecordId
);
}
/**
* Execute a search request against HubSpot CRM objects with rate limiting.
*
* @param string $objectType The object type ('deals', 'companies', 'contacts', 'calls')
* @param array<string, mixed> $payload The search payload with filters, sorts, properties, etc.
* @return array The search response with 'results', 'total', 'paging' keys
* @throws RateLimitException When rate limit is hit
* @throws HubspotException On API errors
*/
public function search(string $objectType, array $payload): array
{
$endpoint = self::BASE_URL . "/crm/v3/objects/{$objectType}/search";
return $this->executeRequest(function () use ($endpoint, $payload) {
$response = $this->getInstance()->getClient()->request('POST', $endpoint, ['json' => $payload]);
return $response->toArray();
});
}
/**
* @throws DealApiException
* @throws CrmException
*/
public function getOpportunityById(string $crmId, array $fields): array
{
try {
// $deal = $this->executeRequest(fn () => $this->getNewInstance()->crm()->deals()->basicApi()->getById(
$deal = $this->getNewInstance()->crm()->deals()->basicApi()->getById(
$crmId,
implode(',', $fields),
'companies,contacts'
);
} catch (DealApiException $e) {
$this->log->info('[Hubspot] Failed to fetch opportunity', [
'crm_id' => $crmId,
'reason' => $e->getMessage(),
]);
throw $e;
}
if (! $deal instanceof DealWithAssociations) {
throw new CrmException('Deal not found');
}
return [
'id' => $deal->getId(),
'properties' => $deal->getProperties(),
'associations' => $deal->getAssociations(),
];
}
/**
* Generic batch read method for HubSpot objects
*
* @param string $objectType The object type ('deals', 'companies', 'contacts')
* @param array<string> $crmIds Array of HubSpot object IDs (max 100)
* @param array<string> $fields Array of property names to fetch
*
* @return array<string, array> Array keyed by CRM ID with object data
*/
private function batchReadObjects(string $objectType, array $crmIds, array $fields): array
{
if (empty($crmIds)) {
return [];
}
$this->validateBatchSize($objectType, $crmIds);
$this->ensureValidToken();
try {
$batchConfig = $this->createBatchConfiguration($objectType);
$batchReadRequest = $this->prepareBatchRequest($batchConfig, $crmIds, $fields);
$response = $batchConfig['api']->read($batchReadRequest);
$this->validateApiResponse($response, $objectType);
$results = $this->processApiResults($response);
$this->logBatchResults($objectType, $crmIds, $results);
return $results;
} catch (\Throwable $e) {
$this->handleBatchError($e, $objectType, $crmIds);
}
}
private function validateBatchSize(string $objectType, array $crmIds): void
{
if (count($crmIds) > 100) {
throw new \InvalidArgumentException("Batch size cannot exceed 100 {$objectType}");
}
}
private function createBatchConfiguration(string $objectType): array
{
$configurations = [
'deals' => [
'batchReadRequest' => new \HubSpot\Client\Crm\Deals\Model\BatchReadInputSimplePublicObjectId(),
'inputClass' => \HubSpot\Client\Crm\Deals\Model\SimplePublicObjectId::class,
'api' => $this->getNewInstance()->crm()->deals()->batchApi(),
],
'companies' => [
'batchReadRequest' => new \HubSpot\Client\Crm\Companies\Model\BatchReadInputSimplePublicObjectId(),
'inputClass' => \HubSpot\Client\Crm\Companies\Model\SimplePublicObjectId::class,
'api' => $this->getNewInstance()->crm()->companies()->batchApi(),
],
'contacts' => [
'batchReadRequest' => new \HubSpot\Client\Crm\Contacts\Model\BatchReadInputSimplePublicObjectId(),
'inputClass' => \HubSpot\Client\Crm\Contacts\Model\SimplePublicObjectId::class,
'api' => $this->getNewInstance()->crm()->contacts()->batchApi(),
],
];
if (! isset($configurations[$objectType])) {
throw new \InvalidArgumentException("Unsupported object type: {$objectType}");
}
return $configurations[$objectType];
}
private function prepareBatchRequest(array $batchConfig, array $crmIds, array $fields): object
{
$batchReadRequest = $batchConfig['batchReadRequest'];
$inputClass = $batchConfig['inputClass'];
$inputs = array_map(function ($crmId) use ($inputClass) {
$input = new $inputClass();
$input->setId($crmId);
return $input;
}, $crmIds);
$batchReadRequest->setInputs($inputs);
$batchReadRequest->setProperties($fields);
return $batchReadRequest;
}
private function validateApiResponse($response, string $objectType): void
{
if (! $response) {
throw new CrmException("HubSpot API returned null response for {$objectType} batch read");
}
}
private function processApiResults($response): array
{
$results = [];
$responseResults = $response->getResults();
if ($responseResults) {
foreach ($responseResults as $object) {
if ($object && $object->getId()) {
$results[$object->getId()] = [
'id' => $object->getId(),
'properties' => $object->getProperties() ?: [],
];
}
}
}
return $results;
}
private function logBatchResults(string $objectType, array $crmIds, array $results): void
{
$this->log->info("[HubSpot] Batch fetched {$objectType}", [
'requested_count' => count($crmIds),
'returned_count' => count($results),
'crm_ids' => $crmIds,
]);
}
private function handleBatchError(\Throwable $e, string $objectType, array $crmIds): void
{
$errorMessage = $e->getMessage() ?: 'Unknown error';
$errorTrace = $e->getTraceAsString() ?: 'No trace available';
$this->log->error("[HubSpot] Failed to batch fetch {$objectType}", [
'crm_ids' => $crmIds,
'error' => $errorMessage,
'trace' => $errorTrace,
]);
throw new CrmException("Failed to batch fetch {$objectType}: " . $errorMessage);
}
/**
* Batch read multiple opportunities by their CRM IDs
*
* @param array<string> $crmIds Array of HubSpot deal IDs (max 100)
* @param array<string> $fields Array of property names to fetch
*
* @return array<string, array> Array keyed by CRM ID with opportunity data
*/
public function getOpportunitiesByIds(array $crmIds, array $fields): array
{
return $this->batchReadObjects('deals', $crmIds, $fields);
}
/**
* Batch read multiple companies by their CRM IDs
*
* @param array<string> $crmIds Array of HubSpot company IDs (max 100)
* @param array<string> $fields Array of property names to fetch
*
* @return array<string, array> Array keyed by CRM ID with company data
*/
public function getCompaniesByIds(array $crmIds, array $fields): array
{
return $this->batchReadObjects('companies', $crmIds, $fields);
}
/**
* Batch read multiple contacts by their CRM IDs
*
* @param array<string> $crmIds Array of HubSpot contact IDs (max 100)
* @param array<string> $fields Array of property names to fetch
*
* @return array<string, array> Array keyed by CRM ID with contact data
*/
public function getContactsByIds(array $crmIds, array $fields): array
{
return $this->batchReadObjects('contacts', $crmIds, $fields);
}
/**
* @throws CompanyApiException
* @throws CrmException
*/
public function getAccountById(string $crmId, array $fields): array
{
try {
$company = $this->getNewInstance()->crm()->companies()->basicApi()->getById(
$crmId,
implode(',', $fields),
);
} catch (CompanyApiException $e) {
$this->log->info('[Hubspot] Failed to fetch account', [
'crm_id' => $crmId,
'reason' => $e->getMessage(),
]);
throw $e;
}
if (! $company instanceof CompaniesWithAssociations) {
throw new CrmException('Account not found');
}
return [
'id' => $company->getId(),
'properties' => $company->getProperties(),
];
}
/**
* @throws ContactApiException
* @throws CrmException
*/
public function getContactById(string $crmId, array $fields): array
{
try {
$contact = $this->getNewInstance()->crm()->contacts()->basicApi()->getById(
$crmId,
implode(',', $fields)
);
} catch (ContactApiException $e) {
$this->log->info('[Hubspot] Failed to fetch contact', [
'crm_id' => $crmId,
'reason' => $e->getMessage(),
]);
throw $e;
}
if (! $contact instanceof ContactsWithAssociations) {
throw new CrmException('Contact not found');
}
return [
'id' => $contact->getId(),
'properties' => $contact->getProperties(),
];
}
/**
* This is email search request that Hubspot offers as GET (more generous quota)
*/
public function getContactByEmail(string $email, array $fields = []): array
{
try {
$contact = $this->getNewInstance()->crm()->contacts()->basicApi()->getById(
$email,
implode(',', $fields),
null,
false,
'email'
);
return [
'id' => $contact->getId(),
'properties' => $contact->getProperties(),
];
} catch (ContactApiException $e) {
$this->log->info('[Hubspot] Failed to fetch contact', [
'email' => $email,
'reason' => $e->getMessage(),
]);
return [];
}
}
/**
* @throws CrmException
*/
public function fetchProperty(string $objectType, string $propertyId): Property
{
$result = $this->getNewInstance()->crm()->properties()->coreApi()->getByName($objectType, $propertyId);
if (! $result instanceof Property) {
$this->log->error('[Hubspot] Failed to fetch property', [
'object_type' => $objectType,
'property_id' => $propertyId,
'reason' => $result->getMessage(),
]);
throw new CrmException('Failed to fetch property');
}
return $result;
}
/**
* @return array<CrmFieldOption>
*/
public function fetchPropertyOptions(string $objectType, string $propertyId): array
{
/** @var array<CrmFieldOption> */
return $this->fetchProperty($objectType, $propertyId)->getOptions();
}
/**
* @return array<array{id:string, label:string, deleted:bool}>
*/
public function fetchCallDispositions(): array
{
/** @var Response $response */
$response = $this->getInstance()->engagements()->getCallDispositions();
/**
* @var array<array{
* id:string,
* label:string,
* deleted: bool
* }>
*/
return $response->toArray();
}
/**
* @return array<CrmFieldOption>
*/
public function fetchOpportunityPipelineStages(): array
{
$stages = [];
$apiResponse = $this->getNewInstance()->crm()->pipelines()->pipelinesApi()->getAll('deals');
if ($apiResponse instanceof Error) {
$this->log->error('[Hubspot] Failed to fetch opportunity pipelines', [
'reason' => $apiResponse->getMessage(),
]);
return [];
}
foreach ($apiResponse->getResults() as $pipeline) {
$pipelineStages = array_map(
static function (PipelineStage $stage) {
return [
'id' => $stage->getId(),
'label' => $stage->getLabel(),
];
},
$pipeline->getStages()
);
$stages = array_merge($stages, $pipelineStages);
}
return $stages;
}
public function fetchOpportunityPipelines(): array
{
$pipelines = [];
try {
$apiResponse = $this->makeRequest('/crm/v3/pipelines/deals');
} catch (\Exception $e) {
$this->log->info('[Hubspot] Failed to fetch opportunity pipelines', [
'reason' => $e->getMessage(),
]);
return [];
}
$response = $apiResponse->toArray();
foreach ($response['results'] as $pipeline) {
$pipelines[] = [
'id' => $pipeline['id'],
'label' => $pipeline['label'],
];
}
return $pipelines;
}
/**
* @return array<CrmFieldOption>
*/
public function fetchMeetingOutcomeFieldOptions(Field $field): array
{
return $field->getCrmProviderId() === 'meetingOutcome'
? $this->fetchMeetingOutcomeTypes()
: $this->fetchCallActivityTypes();
}
public function fetchMeetingOutcomeTypes(): array
{
return $this->extractMeetingTypeOptions(
'[URL_WITH_CREDENTIALS] Response $response */
$response = $this->getInstance()
->getClient()
->request('GET', $endpoint);
/**
* @var array<array{
* value: string,
* label: string,
* displayOrder: int
* }> $optionData
*/
$optionData = $response->toArray()['options'] ?? [];
$options = [];
foreach ($optionData as $item) {
$options[] = [
'id' => $item['value'],
'value' => $item['value'],
'label' => $item['label'],
'display_order' => $item['displayOrder'],
];
}
return $options;
}
/**
* @return array<CrmFieldOption>
*/
public function fetchDispositionFieldOptions(): array
{
$options = [];
$dispositions = $this->fetchCallDispositions();
foreach ($dispositions as $disposition) {
if ($disposition['deleted'] !== false) {
continue;
}
$option['value'] = $disposition['id'];
$option['id'] = $disposition['id'];
$option['label'] = $disposition['label'];
$options[] = $option;
}
return $options;
}
/**
* @return array<CrmFieldOption>
*/
public function fetchOpportunityFieldOptions(Field $field): array
{
if ($field->isStageField()) {
return $this->fetchOpportunityPipelineStages();
}
if ($field->isPipelineField()) {
return $this->fetchOpportunityPipelines();
}
return $this->fetchPropertyOptions('deals', $field->getCrmProviderId());
}
/**
* @throws BadRequest
* @throws HubspotException
*/
public function makeRequest(string $endpoint, $method = 'GET', $payload = [], ?string $queryString = null)
{
$endpoint = self::BASE_URL . $endpoint;
if ($method === 'GET') {
return $this->getInstance()->getClient()?->request(
method: $method,
endpoint: $endpoint,
query_string: $queryString
);
} else {
return $this->getInstance()->getClient()->request($method, $endpoint, [
'json' => ($payload),
]);
}
}
/**
* @throws BadRequest
* @throws HubspotException
*/
public function createMeeting(array $payload): Response
{
$endpoint = '/crm/v3/objects/meetings';
return $this->makeRequest($endpoint, 'POST', $payload);
}
/**
* @throws BadRequest
* @throws HubspotException
*/
public function updateMeeting(string $meetingId, array $payload): Response
{
$endpoint = '/crm/v3/objects/meetings/' . $meetingId;
return $this->makeRequest($endpoint, 'PATCH', $payload);
}
/**
* @throws \Exception
*/
public function createNote(
string $body,
string $ownerId,
int $timestamp,
string $objectId,
NoteObject $noteObject
): ?string {
try {
$noteInput = new SimplePublicObjectInput([
'properties' => [
'hs_note_body' => $body,
'hubspot_owner_id' => $ownerId,
'hs_timestamp' => $timestamp,
],
]);
// Create note
$note = $this->getNewInstance()->crm()->objects()->basicApi()->create('note', $noteInput);
$this->getNewInstance()->crm()->objects()->associationsApi()->create(
'note',
$note->getId(),
$this->getNoteObject($noteObject),
$objectId,
$this->getNoteAssociationType($noteObject),
);
return $note->getId();
} catch (\Exception $e) {
$this->log->error('[Hubspot] Failed to create note', [
'objectId' => $objectId,
'noteObject' => $noteObject->getObjectType(),
'reason' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
return null;
}
public function updateEngagement(string $objectId, array $engagement, array $metadata): void
{
$this->getInstance()->engagements()->update($objectId, $engagement, $metadata);
}
public function getEngagementData(string $engagementId): array
{
$engagement = $this->getInstance()->engagements()->get($engagementId);
return $engagement->toArray();
}
public function createEngagement(array $engagement, array $associations, array $metadata): Response
{
return $this->getInstance()
->engagements()
->create($engagement, $associations, $metadata);
}
public function isUnauthorizedException(\Exception $e): bool
{
// Check for specific HubSpot API exception types first
if ($e instanceof BadRequest) {
// BadRequest can contain 401 status codes
return $e->getCode() === 401;
}
// Check for HTTP client exceptions with status codes
if ($e instanceof \GuzzleHttp\Exception\RequestException && $e->hasResponse()) {
$response = $e->getResponse();
if ($response !== null) {
return $response->getStatusCode() === 401;
}
}
// Check for Guzzle HTTP exceptions
if ($e instanceof \GuzzleHttp\Exception\ClientException) {
return $e->getCode() === 401;
}
// Fallback to string matching as last resort, but be more specific
$message = strtolower($e->getMessage());
return str_contains($message, '401 unauthorized') ||
str_contains($message, 'http 401') ||
str_contains($message, 'status code 401') ||
(preg_match('/\b401\b/', $message) && str_contains($message, 'unauthorized'));
}
/**
* Validates and refreshes the access token if needed before API requests.
* This ensures long-running processes don't fail due to token expiration.
*
* @throws SocialAccountTokenInvalidException
*/
public function ensureValidToken(): void
{
if ($this->oauthAccount === null) {
return;
}
$newToken = $this->tokenManager->ensureValidToken($this->oauthAccount);
if ($newToken !== null) {
$this->accessToken = $newToken;
}
}
public function getConfig()
{
return $this->config;
}
// returns only active (archived=false)
public function getOwners(): array
{
return $this->getNewInstance()->crm()->owners()->getAll();
}
/**
* @param bool $archived
*
* @return array<Owner>|[]
*/
public function getOwnersArchived(bool $archived = true): array
{
$endpoint = '/crm/v3/owners';
$queryParams = [
'archived' => $archived ? 'true' : 'false',
];
$queryString = http_build_query($queryParams);
$owners = [];
try {
$response = $this->makeRequest(endpoint: $endpoint, queryString: $queryString);
$responseData = $response?->toArray();
foreach ($responseData['results'] as $result) {
try {
$owners[] = Owner::create($result);
} catch (Throwable $e) {
$this->log->error('[HubSpot] Failed to process owner data', [
'result' => $result,
'error' => $e->getMessage(),
]);
continue;
}
}
} catch (Throwable $e) {
$this->log->error('HubSpot] Failed to fetch owners', [
'archived' => $archived,
'error' => $e->getMessage(),
]);
return [];
}
return $owners;
}
public function getMeeting(string $engagementId): ObjectWithAssociations
{
return $this->getNewInstance()->crm()->objects()->basicApi()
->getById('meeting', $engagementId, null, 'contact,company,deal');
}
public function deleteEngagement(string $engagementId): void
{
$this->getInstance()->engagements()->delete((int) $engagementId);
}
public function getAssociationsData(array $ids, string $fromObject, string $toObject): array
{
$associationData = [];
$idChunks = array_chunk($ids, self::ASSOCIATIONS_BATCH_SIZE_LIMIT);
foreach ($idChunks as $idChunk) {
try {
$batchInput = new \HubSpot\Client\Crm\Associations\Model\BatchInputPublicObjectId();
$batchInput->setInputs(array_map(function ($id) {
$publicObjectId = new \HubSpot\Client\Crm\Associations\Model\PublicObjectId();
$publicObjectId->setId($id);
return $publicObjectId;
}, $idChunk));
$associatedObjectsData = $this
->getNewInstance()
->crm()
->associations()
->batchApi()
->read($fromObject, $toObject, $batchInput);
if ($associatedObjectsData instanceof \HubSpot\Client\Crm\Associations\Model\BatchResponsePublicAssociationMulti) {
foreach ($associatedObjectsData->getResults() as $association) {
$from = $association->getFrom()->getId();
$toAssociations = $association->getTo();
if (! empty($toAssociations)) {
$associationData[$from] = array_map(function ($item) {
return $item->getId();
}, $toAssociations);
}
}
}
// } catch (RateLimitException $e) {
// throw $e;
} catch (\Exception $e) {
$this->log->error('[Hubspot] Failed to fetch associations', [
'from_object' => $fromObject,
'to_object' => $toObject,
'reason' => $e->getMessage(),
]);
}
}
return $associationData;
}
/**
* @throws \Exception
*/
private function getNoteAssociationType(NoteObject $noteObject): string
{
return match($noteObject) {
NoteObject::Opportunity => 'note_to_deal',
NoteObject::Lead, NoteObject::Contact => 'note_to_contact', // or 'note_to_lead' if your portal supports it
NoteObject::Account => 'note_to_company',
NoteObject::Call, NoteObject::Event => throw new \Exception('Not supported'),
};
}
/**
* @throws \Exception
*/
private function getNoteObject(NoteObject $noteObject): string
{
return match($noteObject) {
NoteObject::Opportunity => 'deal',
NoteObject::Lead, NoteObject::Contact => 'contact',
NoteObject::Account => 'company',
NoteObject::Call, NoteObject::Event => throw new \Exception('Not supported'),
};
}
public function addAssociations(string $objectType, string $associationType, array $payload): Response
{
$endpoint = "/crm/v4/associations/$objectType/$associationType/batch/create";
return $this->makeRequest($endpoint, 'POST', $payload);
}
public function removeAssociations(string $objectType, string $associationType, array $payload): Response
{
$endpoint = "/crm/v4/associations/$objectType/$associationType/batch/archive";
return $this->makeRequest($endpoint, 'POST', $payload);
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
19
Previous Highlighted Error...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JY-20725-handle-HS-search-rate-limit, menu","depth":5,"on_screen":true,"help_text":"Git Branch: JY-20725-handle-HS-search-rate-limit","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"HandleHubspotRateLimitTest","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'HandleHubspotRateLimitTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'HandleHubspotRateLimitTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"2","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"67","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"3","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Services\\Crm\\Hubspot;\n\nuse HubSpot\\Client\\Crm\\Deals\\ApiException as DealApiException;\nuse HubSpot\\Client\\Crm\\Contacts\\ApiException as ContactApiException;\nuse HubSpot\\Client\\Crm\\Companies\\ApiException as CompanyApiException;\nuse HubSpot\\Client\\Crm\\Contacts\\Model\\SimplePublicObjectWithAssociations as ContactsWithAssociations;\nuse HubSpot\\Client\\Crm\\Companies\\Model\\SimplePublicObjectWithAssociations as CompaniesWithAssociations;\nuse HubSpot\\Client\\Crm\\Deals\\Model\\SimplePublicObjectWithAssociations as DealWithAssociations;\nuse HubSpot\\Client\\Crm\\Objects\\Model\\SimplePublicObjectInput;\nuse HubSpot\\Client\\Crm\\Objects\\Model\\SimplePublicObjectWithAssociations as ObjectWithAssociations;\nuse HubSpot\\Client\\Crm\\Pipelines\\Model\\Error;\nuse HubSpot\\Client\\Crm\\Pipelines\\Model\\PipelineStage;\nuse HubSpot\\Client\\Crm\\Properties\\Model\\Property;\nuse HubSpot\\Discovery\\Discovery;\nuse Jiminny\\Exceptions\\CrmException;\nuse Jiminny\\Exceptions\\RateLimitException;\nuse Jiminny\\Exceptions\\SocialAccountTokenInvalidException;\nuse Jiminny\\Jobs\\Crm\\NoteObject;\nuse Jiminny\\Models\\Crm\\Field;\nuse Jiminny\\Services\\Crm\\BaseClient;\nuse Jiminny\\Services\\Crm\\Hubspot\\DTO\\Response\\Owner;\nuse Jiminny\\Services\\SocialAccountService;\nuse SevenShores\\Hubspot\\Exceptions\\BadRequest;\nuse SevenShores\\Hubspot\\Exceptions\\HubspotException;\nuse SevenShores\\Hubspot\\Factory;\nuse SevenShores\\Hubspot\\Http\\Response;\nuse Jiminny\\Services\\Crm\\Hubspot\\Pagination\\HubspotPaginationService;\nuse Illuminate\\Support\\Facades\\Redis;\nuse Throwable;\n\n/**\n * @phpstan-type CrmFieldOption array{id:string, label:string, value?:string}\n */\nclass Client extends BaseClient implements HubspotClientInterface\n{\n public const string MIN_API_VERSION = '2';\n\n public const string BASE_URL = 'https://api.hubapi.com';\n\n public const int ASSOCIATIONS_BATCH_SIZE_LIMIT = 1000;\n\n private HubspotPaginationService $paginationService;\n private HubspotTokenManager $tokenManager;\n\n public function __construct(\n SocialAccountService $socialAccountService,\n HubspotPaginationService $paginationService,\n HubspotTokenManager $tokenManager\n ) {\n parent::__construct($socialAccountService);\n $this->paginationService = $paginationService;\n $this->tokenManager = $tokenManager;\n\n $this->setBaseUrl(self::BASE_URL);\n $this->setVersion(self::MIN_API_VERSION);\n }\n\n /**\n * Reacts to a rate limits (429) from HubSpot by translating it\n * into a RateLimitException carrying retry_after.\n *\n * Wrap any outbound HubSpot call (SDK or raw HTTP) like:\n *\n * $this->executeRequest(fn () => $this->getNewInstance()->crm()->...);\n *\n * @template T\n * @param callable(): T $apiCall\n * @return T\n *\n * @throws RateLimitException\n */\n private function executeRequest(callable $apiCall)\n {\n $cacheKey = $this->getRateLimitCacheKey();\n\n $cachedRetryAfter = Redis::get($cacheKey);\n if (is_string($cachedRetryAfter) && is_numeric($cachedRetryAfter)) {\n throw new RateLimitException(\n 'Hubspot rate limit (cached circuit-breaker)',\n (int) $cachedRetryAfter,\n );\n }\n\n try {\n return $apiCall();\n } catch (Throwable $e) {\n if ($this->isHubspotRateLimit($e)) {\n $retryAfter = $this->parseRetryAfter($e);\n\n Redis::setex($cacheKey, $retryAfter, (string) $retryAfter);\n\n $this->log->warning('[Hubspot] Received 429 from API', [\n 'team_id' => $this->config->team_id,\n 'config_id' => $this->config->getId(),\n 'retry_after' => $retryAfter,\n 'message' => $this->parseMessage($e),\n 'reason' => $e->getMessage(),\n ]);\n\n throw new RateLimitException('Hubspot returned 429', $retryAfter, $e);\n }\n\n throw $e;\n }\n }\n\n private function getRateLimitCacheKey(): string\n {\n return sprintf('hubspot:ratelimit:portal:%d', $this->config->getId());\n }\n\n public function isHubspotRateLimit(Throwable $e): bool\n {\n if ($e instanceof BadRequest\n || $e instanceof DealApiException\n || $e instanceof ContactApiException\n || $e instanceof CompanyApiException\n || $e instanceof \\GuzzleHttp\\Exception\\RequestException\n ) {\n return (int) $e->getCode() === 429;\n }\n\n return false;\n }\n\n public function parseRetryAfter(Throwable $e): int\n {\n if (method_exists($e, 'getResponseHeaders')) {\n $headers = $e->getResponseHeaders() ?: [];\n $value = $headers['Retry-After'] ?? $headers['retry-after'] ?? null;\n if (is_array($value)) {\n $value = $value[0] ?? null;\n }\n if (is_numeric($value)) {\n return (int) $value;\n }\n }\n\n $message = strtolower($this->parseMessage($e));\n\n if (str_contains($message, 'daily')) {\n return 600;\n }\n if (str_contains($message, 'ten secondly')) {\n return 10;\n }\n if (str_contains($message, 'secondly')) {\n return 1;\n }\n\n $this->log->warning('[Hubspot] No retry-after header or known message, using default', [\n 'exception_class' => get_class($e),\n 'message' => $message,\n ]);\n\n return 10;\n }\n\n public function parseMessage(Throwable $e): string\n {\n if (! method_exists($e, 'getResponseBody')) {\n return '';\n }\n\n $body = $e->getResponseBody();\n if (is_string($body)) {\n $body = json_decode($body, true) ?? [];\n }\n\n if (! is_array($body)) {\n return '';\n }\n\n return (string) ($body['message'] ?? '');\n }\n\n public function getMinimumApiVersion(): string\n {\n return self::MIN_API_VERSION;\n }\n\n public function getInstance(): Factory\n {\n return new Factory([\n 'key' => $this->accessToken,\n 'oauth2' => true,\n 'base_url' => $this->baseUrl,\n ]);\n }\n\n public function getNewInstance(): Discovery\n {\n return \\HubSpot\\Factory::createWithAccessToken($this->accessToken);\n }\n\n /**\n * Secondly and daily limits for Hubspot API\n *\n * Product Tier: Free & Starter | Professional & Enterprise | API add-on (any tier)\n * Burst: 100/10 seconds | 150/10 seconds | 200/10 seconds\n * Daily: 250,000 | 500,000 | 1,000,000\n *\n * Official documentation states: The search endpoints are rate limited to five requests per second.\n * Since with 5 RPS were still hitting secondly rate limits we lowered it to 4\n */\n public function getPaginatedData(array $payload, string $type, int $offset = 0): array\n {\n $total = 0;\n $lastId = null;\n $rows = [];\n foreach ($this->getPaginatedDataGenerator($payload, $type, $offset, $total, $lastId) as $row) {\n $rows[] = $row;\n }\n\n return ['results' => $rows, 'total' => $total, 'last_record' => $lastId];\n }\n\n /**\n * @throws HubspotException\n * @throws SocialAccountTokenInvalidException\n * @throws BadRequest\n */\n public function getPaginatedDataGenerator(\n array $payload,\n string $type,\n int $offset = 0,\n int &$total = 0,\n ?string &$lastRecordId = null\n ): \\Generator {\n return $this->paginationService->getPaginatedDataGenerator(\n $this,\n $payload,\n $type,\n $offset,\n $total,\n $lastRecordId\n );\n }\n\n /**\n * Execute a search request against HubSpot CRM objects with rate limiting.\n *\n * @param string $objectType The object type ('deals', 'companies', 'contacts', 'calls')\n * @param array<string, mixed> $payload The search payload with filters, sorts, properties, etc.\n * @return array The search response with 'results', 'total', 'paging' keys\n * @throws RateLimitException When rate limit is hit\n * @throws HubspotException On API errors\n */\n public function search(string $objectType, array $payload): array\n {\n $endpoint = self::BASE_URL . \"/crm/v3/objects/{$objectType}/search\";\n\n return $this->executeRequest(function () use ($endpoint, $payload) {\n $response = $this->getInstance()->getClient()->request('POST', $endpoint, ['json' => $payload]);\n\n return $response->toArray();\n });\n }\n\n /**\n * @throws DealApiException\n * @throws CrmException\n */\n public function getOpportunityById(string $crmId, array $fields): array\n {\n try {\n// $deal = $this->executeRequest(fn () => $this->getNewInstance()->crm()->deals()->basicApi()->getById(\n $deal = $this->getNewInstance()->crm()->deals()->basicApi()->getById(\n $crmId,\n implode(',', $fields),\n 'companies,contacts'\n );\n } catch (DealApiException $e) {\n $this->log->info('[Hubspot] Failed to fetch opportunity', [\n 'crm_id' => $crmId,\n 'reason' => $e->getMessage(),\n ]);\n\n throw $e;\n }\n\n if (! $deal instanceof DealWithAssociations) {\n throw new CrmException('Deal not found');\n }\n\n return [\n 'id' => $deal->getId(),\n 'properties' => $deal->getProperties(),\n 'associations' => $deal->getAssociations(),\n ];\n }\n\n /**\n * Generic batch read method for HubSpot objects\n *\n * @param string $objectType The object type ('deals', 'companies', 'contacts')\n * @param array<string> $crmIds Array of HubSpot object IDs (max 100)\n * @param array<string> $fields Array of property names to fetch\n *\n * @return array<string, array> Array keyed by CRM ID with object data\n */\n private function batchReadObjects(string $objectType, array $crmIds, array $fields): array\n {\n if (empty($crmIds)) {\n return [];\n }\n\n $this->validateBatchSize($objectType, $crmIds);\n $this->ensureValidToken();\n\n try {\n $batchConfig = $this->createBatchConfiguration($objectType);\n $batchReadRequest = $this->prepareBatchRequest($batchConfig, $crmIds, $fields);\n $response = $batchConfig['api']->read($batchReadRequest);\n\n $this->validateApiResponse($response, $objectType);\n\n $results = $this->processApiResults($response);\n $this->logBatchResults($objectType, $crmIds, $results);\n\n return $results;\n } catch (\\Throwable $e) {\n $this->handleBatchError($e, $objectType, $crmIds);\n }\n }\n\n private function validateBatchSize(string $objectType, array $crmIds): void\n {\n if (count($crmIds) > 100) {\n throw new \\InvalidArgumentException(\"Batch size cannot exceed 100 {$objectType}\");\n }\n }\n\n private function createBatchConfiguration(string $objectType): array\n {\n $configurations = [\n 'deals' => [\n 'batchReadRequest' => new \\HubSpot\\Client\\Crm\\Deals\\Model\\BatchReadInputSimplePublicObjectId(),\n 'inputClass' => \\HubSpot\\Client\\Crm\\Deals\\Model\\SimplePublicObjectId::class,\n 'api' => $this->getNewInstance()->crm()->deals()->batchApi(),\n ],\n 'companies' => [\n 'batchReadRequest' => new \\HubSpot\\Client\\Crm\\Companies\\Model\\BatchReadInputSimplePublicObjectId(),\n 'inputClass' => \\HubSpot\\Client\\Crm\\Companies\\Model\\SimplePublicObjectId::class,\n 'api' => $this->getNewInstance()->crm()->companies()->batchApi(),\n ],\n 'contacts' => [\n 'batchReadRequest' => new \\HubSpot\\Client\\Crm\\Contacts\\Model\\BatchReadInputSimplePublicObjectId(),\n 'inputClass' => \\HubSpot\\Client\\Crm\\Contacts\\Model\\SimplePublicObjectId::class,\n 'api' => $this->getNewInstance()->crm()->contacts()->batchApi(),\n ],\n ];\n\n if (! isset($configurations[$objectType])) {\n throw new \\InvalidArgumentException(\"Unsupported object type: {$objectType}\");\n }\n\n return $configurations[$objectType];\n }\n\n private function prepareBatchRequest(array $batchConfig, array $crmIds, array $fields): object\n {\n $batchReadRequest = $batchConfig['batchReadRequest'];\n $inputClass = $batchConfig['inputClass'];\n\n $inputs = array_map(function ($crmId) use ($inputClass) {\n $input = new $inputClass();\n $input->setId($crmId);\n\n return $input;\n }, $crmIds);\n\n $batchReadRequest->setInputs($inputs);\n $batchReadRequest->setProperties($fields);\n\n return $batchReadRequest;\n }\n\n private function validateApiResponse($response, string $objectType): void\n {\n if (! $response) {\n throw new CrmException(\"HubSpot API returned null response for {$objectType} batch read\");\n }\n }\n\n private function processApiResults($response): array\n {\n $results = [];\n $responseResults = $response->getResults();\n\n if ($responseResults) {\n foreach ($responseResults as $object) {\n if ($object && $object->getId()) {\n $results[$object->getId()] = [\n 'id' => $object->getId(),\n 'properties' => $object->getProperties() ?: [],\n ];\n }\n }\n }\n\n return $results;\n }\n\n private function logBatchResults(string $objectType, array $crmIds, array $results): void\n {\n $this->log->info(\"[HubSpot] Batch fetched {$objectType}\", [\n 'requested_count' => count($crmIds),\n 'returned_count' => count($results),\n 'crm_ids' => $crmIds,\n ]);\n }\n\n private function handleBatchError(\\Throwable $e, string $objectType, array $crmIds): void\n {\n $errorMessage = $e->getMessage() ?: 'Unknown error';\n $errorTrace = $e->getTraceAsString() ?: 'No trace available';\n\n $this->log->error(\"[HubSpot] Failed to batch fetch {$objectType}\", [\n 'crm_ids' => $crmIds,\n 'error' => $errorMessage,\n 'trace' => $errorTrace,\n ]);\n\n throw new CrmException(\"Failed to batch fetch {$objectType}: \" . $errorMessage);\n }\n\n /**\n * Batch read multiple opportunities by their CRM IDs\n *\n * @param array<string> $crmIds Array of HubSpot deal IDs (max 100)\n * @param array<string> $fields Array of property names to fetch\n *\n * @return array<string, array> Array keyed by CRM ID with opportunity data\n */\n public function getOpportunitiesByIds(array $crmIds, array $fields): array\n {\n return $this->batchReadObjects('deals', $crmIds, $fields);\n }\n\n /**\n * Batch read multiple companies by their CRM IDs\n *\n * @param array<string> $crmIds Array of HubSpot company IDs (max 100)\n * @param array<string> $fields Array of property names to fetch\n *\n * @return array<string, array> Array keyed by CRM ID with company data\n */\n public function getCompaniesByIds(array $crmIds, array $fields): array\n {\n return $this->batchReadObjects('companies', $crmIds, $fields);\n }\n\n /**\n * Batch read multiple contacts by their CRM IDs\n *\n * @param array<string> $crmIds Array of HubSpot contact IDs (max 100)\n * @param array<string> $fields Array of property names to fetch\n *\n * @return array<string, array> Array keyed by CRM ID with contact data\n */\n public function getContactsByIds(array $crmIds, array $fields): array\n {\n return $this->batchReadObjects('contacts', $crmIds, $fields);\n }\n\n /**\n * @throws CompanyApiException\n * @throws CrmException\n */\n public function getAccountById(string $crmId, array $fields): array\n {\n try {\n $company = $this->getNewInstance()->crm()->companies()->basicApi()->getById(\n $crmId,\n implode(',', $fields),\n );\n } catch (CompanyApiException $e) {\n $this->log->info('[Hubspot] Failed to fetch account', [\n 'crm_id' => $crmId,\n 'reason' => $e->getMessage(),\n ]);\n\n throw $e;\n }\n\n if (! $company instanceof CompaniesWithAssociations) {\n throw new CrmException('Account not found');\n }\n\n return [\n 'id' => $company->getId(),\n 'properties' => $company->getProperties(),\n ];\n }\n\n /**\n * @throws ContactApiException\n * @throws CrmException\n */\n public function getContactById(string $crmId, array $fields): array\n {\n try {\n $contact = $this->getNewInstance()->crm()->contacts()->basicApi()->getById(\n $crmId,\n implode(',', $fields)\n );\n } catch (ContactApiException $e) {\n $this->log->info('[Hubspot] Failed to fetch contact', [\n 'crm_id' => $crmId,\n 'reason' => $e->getMessage(),\n ]);\n\n throw $e;\n }\n\n if (! $contact instanceof ContactsWithAssociations) {\n throw new CrmException('Contact not found');\n }\n\n return [\n 'id' => $contact->getId(),\n 'properties' => $contact->getProperties(),\n ];\n }\n\n /**\n * This is email search request that Hubspot offers as GET (more generous quota)\n */\n public function getContactByEmail(string $email, array $fields = []): array\n {\n try {\n $contact = $this->getNewInstance()->crm()->contacts()->basicApi()->getById(\n $email,\n implode(',', $fields),\n null,\n false,\n 'email'\n );\n\n return [\n 'id' => $contact->getId(),\n 'properties' => $contact->getProperties(),\n ];\n } catch (ContactApiException $e) {\n $this->log->info('[Hubspot] Failed to fetch contact', [\n 'email' => $email,\n 'reason' => $e->getMessage(),\n ]);\n\n return [];\n }\n }\n\n /**\n * @throws CrmException\n */\n public function fetchProperty(string $objectType, string $propertyId): Property\n {\n $result = $this->getNewInstance()->crm()->properties()->coreApi()->getByName($objectType, $propertyId);\n\n if (! $result instanceof Property) {\n $this->log->error('[Hubspot] Failed to fetch property', [\n 'object_type' => $objectType,\n 'property_id' => $propertyId,\n 'reason' => $result->getMessage(),\n ]);\n\n throw new CrmException('Failed to fetch property');\n }\n\n return $result;\n }\n\n /**\n * @return array<CrmFieldOption>\n */\n public function fetchPropertyOptions(string $objectType, string $propertyId): array\n {\n /** @var array<CrmFieldOption> */\n return $this->fetchProperty($objectType, $propertyId)->getOptions();\n }\n\n /**\n * @return array<array{id:string, label:string, deleted:bool}>\n */\n public function fetchCallDispositions(): array\n {\n /** @var Response $response */\n $response = $this->getInstance()->engagements()->getCallDispositions();\n\n /**\n * @var array<array{\n * id:string,\n * label:string,\n * deleted: bool\n * }>\n */\n return $response->toArray();\n }\n\n /**\n * @return array<CrmFieldOption>\n */\n public function fetchOpportunityPipelineStages(): array\n {\n $stages = [];\n $apiResponse = $this->getNewInstance()->crm()->pipelines()->pipelinesApi()->getAll('deals');\n\n if ($apiResponse instanceof Error) {\n $this->log->error('[Hubspot] Failed to fetch opportunity pipelines', [\n 'reason' => $apiResponse->getMessage(),\n ]);\n\n return [];\n }\n\n foreach ($apiResponse->getResults() as $pipeline) {\n $pipelineStages = array_map(\n static function (PipelineStage $stage) {\n return [\n 'id' => $stage->getId(),\n 'label' => $stage->getLabel(),\n ];\n },\n $pipeline->getStages()\n );\n\n $stages = array_merge($stages, $pipelineStages);\n }\n\n return $stages;\n }\n\n public function fetchOpportunityPipelines(): array\n {\n $pipelines = [];\n\n try {\n $apiResponse = $this->makeRequest('/crm/v3/pipelines/deals');\n } catch (\\Exception $e) {\n $this->log->info('[Hubspot] Failed to fetch opportunity pipelines', [\n 'reason' => $e->getMessage(),\n ]);\n\n return [];\n }\n\n $response = $apiResponse->toArray();\n\n foreach ($response['results'] as $pipeline) {\n $pipelines[] = [\n 'id' => $pipeline['id'],\n 'label' => $pipeline['label'],\n ];\n }\n\n return $pipelines;\n }\n\n /**\n * @return array<CrmFieldOption>\n */\n public function fetchMeetingOutcomeFieldOptions(Field $field): array\n {\n return $field->getCrmProviderId() === 'meetingOutcome'\n ? $this->fetchMeetingOutcomeTypes()\n : $this->fetchCallActivityTypes();\n }\n\n public function fetchMeetingOutcomeTypes(): array\n {\n return $this->extractMeetingTypeOptions(\n 'https://api.hubapi.com/crm/v3/properties/meeting/hs_meeting_outcome'\n );\n }\n\n public function fetchCallActivityTypes(): array\n {\n return $this->extractMeetingTypeOptions(\n 'https://api.hubapi.com/crm/v3/properties/call/hs_activity_type'\n );\n }\n\n private function extractMeetingTypeOptions(string $endpoint): array\n {\n /** @var Response $response */\n $response = $this->getInstance()\n ->getClient()\n ->request('GET', $endpoint);\n\n /**\n * @var array<array{\n * value: string,\n * label: string,\n * displayOrder: int\n * }> $optionData\n */\n $optionData = $response->toArray()['options'] ?? [];\n\n $options = [];\n foreach ($optionData as $item) {\n $options[] = [\n 'id' => $item['value'],\n 'value' => $item['value'],\n 'label' => $item['label'],\n 'display_order' => $item['displayOrder'],\n ];\n }\n\n return $options;\n }\n\n /**\n * @return array<CrmFieldOption>\n */\n public function fetchDispositionFieldOptions(): array\n {\n $options = [];\n\n $dispositions = $this->fetchCallDispositions();\n\n foreach ($dispositions as $disposition) {\n if ($disposition['deleted'] !== false) {\n continue;\n }\n\n $option['value'] = $disposition['id'];\n $option['id'] = $disposition['id'];\n $option['label'] = $disposition['label'];\n\n $options[] = $option;\n }\n\n return $options;\n }\n\n /**\n * @return array<CrmFieldOption>\n */\n public function fetchOpportunityFieldOptions(Field $field): array\n {\n if ($field->isStageField()) {\n return $this->fetchOpportunityPipelineStages();\n }\n\n if ($field->isPipelineField()) {\n return $this->fetchOpportunityPipelines();\n }\n\n return $this->fetchPropertyOptions('deals', $field->getCrmProviderId());\n }\n\n /**\n * @throws BadRequest\n * @throws HubspotException\n */\n public function makeRequest(string $endpoint, $method = 'GET', $payload = [], ?string $queryString = null)\n {\n $endpoint = self::BASE_URL . $endpoint;\n\n if ($method === 'GET') {\n return $this->getInstance()->getClient()?->request(\n method: $method,\n endpoint: $endpoint,\n query_string: $queryString\n );\n } else {\n return $this->getInstance()->getClient()->request($method, $endpoint, [\n 'json' => ($payload),\n ]);\n }\n }\n\n /**\n * @throws BadRequest\n * @throws HubspotException\n */\n public function createMeeting(array $payload): Response\n {\n $endpoint = '/crm/v3/objects/meetings';\n\n return $this->makeRequest($endpoint, 'POST', $payload);\n }\n\n /**\n * @throws BadRequest\n * @throws HubspotException\n */\n public function updateMeeting(string $meetingId, array $payload): Response\n {\n $endpoint = '/crm/v3/objects/meetings/' . $meetingId;\n\n return $this->makeRequest($endpoint, 'PATCH', $payload);\n }\n\n /**\n * @throws \\Exception\n */\n public function createNote(\n string $body,\n string $ownerId,\n int $timestamp,\n string $objectId,\n NoteObject $noteObject\n ): ?string {\n try {\n $noteInput = new SimplePublicObjectInput([\n 'properties' => [\n 'hs_note_body' => $body,\n 'hubspot_owner_id' => $ownerId,\n 'hs_timestamp' => $timestamp,\n ],\n ]);\n\n // Create note\n $note = $this->getNewInstance()->crm()->objects()->basicApi()->create('note', $noteInput);\n\n $this->getNewInstance()->crm()->objects()->associationsApi()->create(\n 'note',\n $note->getId(),\n $this->getNoteObject($noteObject),\n $objectId,\n $this->getNoteAssociationType($noteObject),\n );\n\n return $note->getId();\n } catch (\\Exception $e) {\n $this->log->error('[Hubspot] Failed to create note', [\n 'objectId' => $objectId,\n 'noteObject' => $noteObject->getObjectType(),\n 'reason' => $e->getMessage(),\n ]);\n\n \\Sentry::captureException($e);\n }\n\n return null;\n }\n\n public function updateEngagement(string $objectId, array $engagement, array $metadata): void\n {\n $this->getInstance()->engagements()->update($objectId, $engagement, $metadata);\n }\n\n public function getEngagementData(string $engagementId): array\n {\n $engagement = $this->getInstance()->engagements()->get($engagementId);\n\n return $engagement->toArray();\n }\n\n public function createEngagement(array $engagement, array $associations, array $metadata): Response\n {\n return $this->getInstance()\n ->engagements()\n ->create($engagement, $associations, $metadata);\n }\n\n public function isUnauthorizedException(\\Exception $e): bool\n {\n // Check for specific HubSpot API exception types first\n if ($e instanceof BadRequest) {\n // BadRequest can contain 401 status codes\n return $e->getCode() === 401;\n }\n\n // Check for HTTP client exceptions with status codes\n if ($e instanceof \\GuzzleHttp\\Exception\\RequestException && $e->hasResponse()) {\n $response = $e->getResponse();\n if ($response !== null) {\n return $response->getStatusCode() === 401;\n }\n }\n\n // Check for Guzzle HTTP exceptions\n if ($e instanceof \\GuzzleHttp\\Exception\\ClientException) {\n return $e->getCode() === 401;\n }\n\n // Fallback to string matching as last resort, but be more specific\n $message = strtolower($e->getMessage());\n\n return str_contains($message, '401 unauthorized') ||\n str_contains($message, 'http 401') ||\n str_contains($message, 'status code 401') ||\n (preg_match('/\\b401\\b/', $message) && str_contains($message, 'unauthorized'));\n }\n\n /**\n * Validates and refreshes the access token if needed before API requests.\n * This ensures long-running processes don't fail due to token expiration.\n *\n * @throws SocialAccountTokenInvalidException\n */\n public function ensureValidToken(): void\n {\n if ($this->oauthAccount === null) {\n return;\n }\n\n $newToken = $this->tokenManager->ensureValidToken($this->oauthAccount);\n if ($newToken !== null) {\n $this->accessToken = $newToken;\n }\n }\n\n public function getConfig()\n {\n return $this->config;\n }\n\n // returns only active (archived=false)\n public function getOwners(): array\n {\n return $this->getNewInstance()->crm()->owners()->getAll();\n }\n\n /**\n * @param bool $archived\n *\n * @return array<Owner>|[]\n */\n public function getOwnersArchived(bool $archived = true): array\n {\n $endpoint = '/crm/v3/owners';\n $queryParams = [\n 'archived' => $archived ? 'true' : 'false',\n ];\n $queryString = http_build_query($queryParams);\n\n $owners = [];\n\n try {\n $response = $this->makeRequest(endpoint: $endpoint, queryString: $queryString);\n $responseData = $response?->toArray();\n\n foreach ($responseData['results'] as $result) {\n try {\n $owners[] = Owner::create($result);\n } catch (Throwable $e) {\n $this->log->error('[HubSpot] Failed to process owner data', [\n 'result' => $result,\n 'error' => $e->getMessage(),\n ]);\n\n continue;\n }\n }\n } catch (Throwable $e) {\n $this->log->error('HubSpot] Failed to fetch owners', [\n 'archived' => $archived,\n 'error' => $e->getMessage(),\n ]);\n\n return [];\n }\n\n return $owners;\n }\n\n public function getMeeting(string $engagementId): ObjectWithAssociations\n {\n return $this->getNewInstance()->crm()->objects()->basicApi()\n ->getById('meeting', $engagementId, null, 'contact,company,deal');\n }\n\n public function deleteEngagement(string $engagementId): void\n {\n $this->getInstance()->engagements()->delete((int) $engagementId);\n }\n\n public function getAssociationsData(array $ids, string $fromObject, string $toObject): array\n {\n $associationData = [];\n $idChunks = array_chunk($ids, self::ASSOCIATIONS_BATCH_SIZE_LIMIT);\n\n foreach ($idChunks as $idChunk) {\n try {\n $batchInput = new \\HubSpot\\Client\\Crm\\Associations\\Model\\BatchInputPublicObjectId();\n $batchInput->setInputs(array_map(function ($id) {\n $publicObjectId = new \\HubSpot\\Client\\Crm\\Associations\\Model\\PublicObjectId();\n $publicObjectId->setId($id);\n\n return $publicObjectId;\n }, $idChunk));\n\n $associatedObjectsData = $this\n ->getNewInstance()\n ->crm()\n ->associations()\n ->batchApi()\n ->read($fromObject, $toObject, $batchInput);\n\n if ($associatedObjectsData instanceof \\HubSpot\\Client\\Crm\\Associations\\Model\\BatchResponsePublicAssociationMulti) {\n foreach ($associatedObjectsData->getResults() as $association) {\n $from = $association->getFrom()->getId();\n $toAssociations = $association->getTo();\n\n if (! empty($toAssociations)) {\n $associationData[$from] = array_map(function ($item) {\n return $item->getId();\n }, $toAssociations);\n }\n }\n }\n// } catch (RateLimitException $e) {\n// throw $e;\n } catch (\\Exception $e) {\n $this->log->error('[Hubspot] Failed to fetch associations', [\n 'from_object' => $fromObject,\n 'to_object' => $toObject,\n 'reason' => $e->getMessage(),\n ]);\n }\n }\n\n return $associationData;\n }\n\n /**\n * @throws \\Exception\n */\n private function getNoteAssociationType(NoteObject $noteObject): string\n {\n return match($noteObject) {\n NoteObject::Opportunity => 'note_to_deal',\n NoteObject::Lead, NoteObject::Contact => 'note_to_contact', // or 'note_to_lead' if your portal supports it\n NoteObject::Account => 'note_to_company',\n NoteObject::Call, NoteObject::Event => throw new \\Exception('Not supported'),\n };\n }\n\n /**\n * @throws \\Exception\n */\n private function getNoteObject(NoteObject $noteObject): string\n {\n return match($noteObject) {\n NoteObject::Opportunity => 'deal',\n NoteObject::Lead, NoteObject::Contact => 'contact',\n NoteObject::Account => 'company',\n NoteObject::Call, NoteObject::Event => throw new \\Exception('Not supported'),\n };\n }\n\n public function addAssociations(string $objectType, string $associationType, array $payload): Response\n {\n $endpoint = \"/crm/v4/associations/$objectType/$associationType/batch/create\";\n\n return $this->makeRequest($endpoint, 'POST', $payload);\n }\n\n public function removeAssociations(string $objectType, string $associationType, array $payload): Response\n {\n $endpoint = \"/crm/v4/associations/$objectType/$associationType/batch/archive\";\n\n return $this->makeRequest($endpoint, 'POST', $payload);\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Services\\Crm\\Hubspot;\n\nuse HubSpot\\Client\\Crm\\Deals\\ApiException as DealApiException;\nuse HubSpot\\Client\\Crm\\Contacts\\ApiException as ContactApiException;\nuse HubSpot\\Client\\Crm\\Companies\\ApiException as CompanyApiException;\nuse HubSpot\\Client\\Crm\\Contacts\\Model\\SimplePublicObjectWithAssociations as ContactsWithAssociations;\nuse HubSpot\\Client\\Crm\\Companies\\Model\\SimplePublicObjectWithAssociations as CompaniesWithAssociations;\nuse HubSpot\\Client\\Crm\\Deals\\Model\\SimplePublicObjectWithAssociations as DealWithAssociations;\nuse HubSpot\\Client\\Crm\\Objects\\Model\\SimplePublicObjectInput;\nuse HubSpot\\Client\\Crm\\Objects\\Model\\SimplePublicObjectWithAssociations as ObjectWithAssociations;\nuse HubSpot\\Client\\Crm\\Pipelines\\Model\\Error;\nuse HubSpot\\Client\\Crm\\Pipelines\\Model\\PipelineStage;\nuse HubSpot\\Client\\Crm\\Properties\\Model\\Property;\nuse HubSpot\\Discovery\\Discovery;\nuse Jiminny\\Exceptions\\CrmException;\nuse Jiminny\\Exceptions\\RateLimitException;\nuse Jiminny\\Exceptions\\SocialAccountTokenInvalidException;\nuse Jiminny\\Jobs\\Crm\\NoteObject;\nuse Jiminny\\Models\\Crm\\Field;\nuse Jiminny\\Services\\Crm\\BaseClient;\nuse Jiminny\\Services\\Crm\\Hubspot\\DTO\\Response\\Owner;\nuse Jiminny\\Services\\SocialAccountService;\nuse SevenShores\\Hubspot\\Exceptions\\BadRequest;\nuse SevenShores\\Hubspot\\Exceptions\\HubspotException;\nuse SevenShores\\Hubspot\\Factory;\nuse SevenShores\\Hubspot\\Http\\Response;\nuse Jiminny\\Services\\Crm\\Hubspot\\Pagination\\HubspotPaginationService;\nuse Illuminate\\Support\\Facades\\Redis;\nuse Throwable;\n\n/**\n * @phpstan-type CrmFieldOption array{id:string, label:string, value?:string}\n */\nclass Client extends BaseClient implements HubspotClientInterface\n{\n public const string MIN_API_VERSION = '2';\n\n public const string BASE_URL = 'https://api.hubapi.com';\n\n public const int ASSOCIATIONS_BATCH_SIZE_LIMIT = 1000;\n\n private HubspotPaginationService $paginationService;\n private HubspotTokenManager $tokenManager;\n\n public function __construct(\n SocialAccountService $socialAccountService,\n HubspotPaginationService $paginationService,\n HubspotTokenManager $tokenManager\n ) {\n parent::__construct($socialAccountService);\n $this->paginationService = $paginationService;\n $this->tokenManager = $tokenManager;\n\n $this->setBaseUrl(self::BASE_URL);\n $this->setVersion(self::MIN_API_VERSION);\n }\n\n /**\n * Reacts to a rate limits (429) from HubSpot by translating it\n * into a RateLimitException carrying retry_after.\n *\n * Wrap any outbound HubSpot call (SDK or raw HTTP) like:\n *\n * $this->executeRequest(fn () => $this->getNewInstance()->crm()->...);\n *\n * @template T\n * @param callable(): T $apiCall\n * @return T\n *\n * @throws RateLimitException\n */\n private function executeRequest(callable $apiCall)\n {\n $cacheKey = $this->getRateLimitCacheKey();\n\n $cachedRetryAfter = Redis::get($cacheKey);\n if (is_string($cachedRetryAfter) && is_numeric($cachedRetryAfter)) {\n throw new RateLimitException(\n 'Hubspot rate limit (cached circuit-breaker)',\n (int) $cachedRetryAfter,\n );\n }\n\n try {\n return $apiCall();\n } catch (Throwable $e) {\n if ($this->isHubspotRateLimit($e)) {\n $retryAfter = $this->parseRetryAfter($e);\n\n Redis::setex($cacheKey, $retryAfter, (string) $retryAfter);\n\n $this->log->warning('[Hubspot] Received 429 from API', [\n 'team_id' => $this->config->team_id,\n 'config_id' => $this->config->getId(),\n 'retry_after' => $retryAfter,\n 'message' => $this->parseMessage($e),\n 'reason' => $e->getMessage(),\n ]);\n\n throw new RateLimitException('Hubspot returned 429', $retryAfter, $e);\n }\n\n throw $e;\n }\n }\n\n private function getRateLimitCacheKey(): string\n {\n return sprintf('hubspot:ratelimit:portal:%d', $this->config->getId());\n }\n\n public function isHubspotRateLimit(Throwable $e): bool\n {\n if ($e instanceof BadRequest\n || $e instanceof DealApiException\n || $e instanceof ContactApiException\n || $e instanceof CompanyApiException\n || $e instanceof \\GuzzleHttp\\Exception\\RequestException\n ) {\n return (int) $e->getCode() === 429;\n }\n\n return false;\n }\n\n public function parseRetryAfter(Throwable $e): int\n {\n if (method_exists($e, 'getResponseHeaders')) {\n $headers = $e->getResponseHeaders() ?: [];\n $value = $headers['Retry-After'] ?? $headers['retry-after'] ?? null;\n if (is_array($value)) {\n $value = $value[0] ?? null;\n }\n if (is_numeric($value)) {\n return (int) $value;\n }\n }\n\n $message = strtolower($this->parseMessage($e));\n\n if (str_contains($message, 'daily')) {\n return 600;\n }\n if (str_contains($message, 'ten secondly')) {\n return 10;\n }\n if (str_contains($message, 'secondly')) {\n return 1;\n }\n\n $this->log->warning('[Hubspot] No retry-after header or known message, using default', [\n 'exception_class' => get_class($e),\n 'message' => $message,\n ]);\n\n return 10;\n }\n\n public function parseMessage(Throwable $e): string\n {\n if (! method_exists($e, 'getResponseBody')) {\n return '';\n }\n\n $body = $e->getResponseBody();\n if (is_string($body)) {\n $body = json_decode($body, true) ?? [];\n }\n\n if (! is_array($body)) {\n return '';\n }\n\n return (string) ($body['message'] ?? '');\n }\n\n public function getMinimumApiVersion(): string\n {\n return self::MIN_API_VERSION;\n }\n\n public function getInstance(): Factory\n {\n return new Factory([\n 'key' => $this->accessToken,\n 'oauth2' => true,\n 'base_url' => $this->baseUrl,\n ]);\n }\n\n public function getNewInstance(): Discovery\n {\n return \\HubSpot\\Factory::createWithAccessToken($this->accessToken);\n }\n\n /**\n * Secondly and daily limits for Hubspot API\n *\n * Product Tier: Free & Starter | Professional & Enterprise | API add-on (any tier)\n * Burst: 100/10 seconds | 150/10 seconds | 200/10 seconds\n * Daily: 250,000 | 500,000 | 1,000,000\n *\n * Official documentation states: The search endpoints are rate limited to five requests per second.\n * Since with 5 RPS were still hitting secondly rate limits we lowered it to 4\n */\n public function getPaginatedData(array $payload, string $type, int $offset = 0): array\n {\n $total = 0;\n $lastId = null;\n $rows = [];\n foreach ($this->getPaginatedDataGenerator($payload, $type, $offset, $total, $lastId) as $row) {\n $rows[] = $row;\n }\n\n return ['results' => $rows, 'total' => $total, 'last_record' => $lastId];\n }\n\n /**\n * @throws HubspotException\n * @throws SocialAccountTokenInvalidException\n * @throws BadRequest\n */\n public function getPaginatedDataGenerator(\n array $payload,\n string $type,\n int $offset = 0,\n int &$total = 0,\n ?string &$lastRecordId = null\n ): \\Generator {\n return $this->paginationService->getPaginatedDataGenerator(\n $this,\n $payload,\n $type,\n $offset,\n $total,\n $lastRecordId\n );\n }\n\n /**\n * Execute a search request against HubSpot CRM objects with rate limiting.\n *\n * @param string $objectType The object type ('deals', 'companies', 'contacts', 'calls')\n * @param array<string, mixed> $payload The search payload with filters, sorts, properties, etc.\n * @return array The search response with 'results', 'total', 'paging' keys\n * @throws RateLimitException When rate limit is hit\n * @throws HubspotException On API errors\n */\n public function search(string $objectType, array $payload): array\n {\n $endpoint = self::BASE_URL . \"/crm/v3/objects/{$objectType}/search\";\n\n return $this->executeRequest(function () use ($endpoint, $payload) {\n $response = $this->getInstance()->getClient()->request('POST', $endpoint, ['json' => $payload]);\n\n return $response->toArray();\n });\n }\n\n /**\n * @throws DealApiException\n * @throws CrmException\n */\n public function getOpportunityById(string $crmId, array $fields): array\n {\n try {\n// $deal = $this->executeRequest(fn () => $this->getNewInstance()->crm()->deals()->basicApi()->getById(\n $deal = $this->getNewInstance()->crm()->deals()->basicApi()->getById(\n $crmId,\n implode(',', $fields),\n 'companies,contacts'\n );\n } catch (DealApiException $e) {\n $this->log->info('[Hubspot] Failed to fetch opportunity', [\n 'crm_id' => $crmId,\n 'reason' => $e->getMessage(),\n ]);\n\n throw $e;\n }\n\n if (! $deal instanceof DealWithAssociations) {\n throw new CrmException('Deal not found');\n }\n\n return [\n 'id' => $deal->getId(),\n 'properties' => $deal->getProperties(),\n 'associations' => $deal->getAssociations(),\n ];\n }\n\n /**\n * Generic batch read method for HubSpot objects\n *\n * @param string $objectType The object type ('deals', 'companies', 'contacts')\n * @param array<string> $crmIds Array of HubSpot object IDs (max 100)\n * @param array<string> $fields Array of property names to fetch\n *\n * @return array<string, array> Array keyed by CRM ID with object data\n */\n private function batchReadObjects(string $objectType, array $crmIds, array $fields): array\n {\n if (empty($crmIds)) {\n return [];\n }\n\n $this->validateBatchSize($objectType, $crmIds);\n $this->ensureValidToken();\n\n try {\n $batchConfig = $this->createBatchConfiguration($objectType);\n $batchReadRequest = $this->prepareBatchRequest($batchConfig, $crmIds, $fields);\n $response = $batchConfig['api']->read($batchReadRequest);\n\n $this->validateApiResponse($response, $objectType);\n\n $results = $this->processApiResults($response);\n $this->logBatchResults($objectType, $crmIds, $results);\n\n return $results;\n } catch (\\Throwable $e) {\n $this->handleBatchError($e, $objectType, $crmIds);\n }\n }\n\n private function validateBatchSize(string $objectType, array $crmIds): void\n {\n if (count($crmIds) > 100) {\n throw new \\InvalidArgumentException(\"Batch size cannot exceed 100 {$objectType}\");\n }\n }\n\n private function createBatchConfiguration(string $objectType): array\n {\n $configurations = [\n 'deals' => [\n 'batchReadRequest' => new \\HubSpot\\Client\\Crm\\Deals\\Model\\BatchReadInputSimplePublicObjectId(),\n 'inputClass' => \\HubSpot\\Client\\Crm\\Deals\\Model\\SimplePublicObjectId::class,\n 'api' => $this->getNewInstance()->crm()->deals()->batchApi(),\n ],\n 'companies' => [\n 'batchReadRequest' => new \\HubSpot\\Client\\Crm\\Companies\\Model\\BatchReadInputSimplePublicObjectId(),\n 'inputClass' => \\HubSpot\\Client\\Crm\\Companies\\Model\\SimplePublicObjectId::class,\n 'api' => $this->getNewInstance()->crm()->companies()->batchApi(),\n ],\n 'contacts' => [\n 'batchReadRequest' => new \\HubSpot\\Client\\Crm\\Contacts\\Model\\BatchReadInputSimplePublicObjectId(),\n 'inputClass' => \\HubSpot\\Client\\Crm\\Contacts\\Model\\SimplePublicObjectId::class,\n 'api' => $this->getNewInstance()->crm()->contacts()->batchApi(),\n ],\n ];\n\n if (! isset($configurations[$objectType])) {\n throw new \\InvalidArgumentException(\"Unsupported object type: {$objectType}\");\n }\n\n return $configurations[$objectType];\n }\n\n private function prepareBatchRequest(array $batchConfig, array $crmIds, array $fields): object\n {\n $batchReadRequest = $batchConfig['batchReadRequest'];\n $inputClass = $batchConfig['inputClass'];\n\n $inputs = array_map(function ($crmId) use ($inputClass) {\n $input = new $inputClass();\n $input->setId($crmId);\n\n return $input;\n }, $crmIds);\n\n $batchReadRequest->setInputs($inputs);\n $batchReadRequest->setProperties($fields);\n\n return $batchReadRequest;\n }\n\n private function validateApiResponse($response, string $objectType): void\n {\n if (! $response) {\n throw new CrmException(\"HubSpot API returned null response for {$objectType} batch read\");\n }\n }\n\n private function processApiResults($response): array\n {\n $results = [];\n $responseResults = $response->getResults();\n\n if ($responseResults) {\n foreach ($responseResults as $object) {\n if ($object && $object->getId()) {\n $results[$object->getId()] = [\n 'id' => $object->getId(),\n 'properties' => $object->getProperties() ?: [],\n ];\n }\n }\n }\n\n return $results;\n }\n\n private function logBatchResults(string $objectType, array $crmIds, array $results): void\n {\n $this->log->info(\"[HubSpot] Batch fetched {$objectType}\", [\n 'requested_count' => count($crmIds),\n 'returned_count' => count($results),\n 'crm_ids' => $crmIds,\n ]);\n }\n\n private function handleBatchError(\\Throwable $e, string $objectType, array $crmIds): void\n {\n $errorMessage = $e->getMessage() ?: 'Unknown error';\n $errorTrace = $e->getTraceAsString() ?: 'No trace available';\n\n $this->log->error(\"[HubSpot] Failed to batch fetch {$objectType}\", [\n 'crm_ids' => $crmIds,\n 'error' => $errorMessage,\n 'trace' => $errorTrace,\n ]);\n\n throw new CrmException(\"Failed to batch fetch {$objectType}: \" . $errorMessage);\n }\n\n /**\n * Batch read multiple opportunities by their CRM IDs\n *\n * @param array<string> $crmIds Array of HubSpot deal IDs (max 100)\n * @param array<string> $fields Array of property names to fetch\n *\n * @return array<string, array> Array keyed by CRM ID with opportunity data\n */\n public function getOpportunitiesByIds(array $crmIds, array $fields): array\n {\n return $this->batchReadObjects('deals', $crmIds, $fields);\n }\n\n /**\n * Batch read multiple companies by their CRM IDs\n *\n * @param array<string> $crmIds Array of HubSpot company IDs (max 100)\n * @param array<string> $fields Array of property names to fetch\n *\n * @return array<string, array> Array keyed by CRM ID with company data\n */\n public function getCompaniesByIds(array $crmIds, array $fields): array\n {\n return $this->batchReadObjects('companies', $crmIds, $fields);\n }\n\n /**\n * Batch read multiple contacts by their CRM IDs\n *\n * @param array<string> $crmIds Array of HubSpot contact IDs (max 100)\n * @param array<string> $fields Array of property names to fetch\n *\n * @return array<string, array> Array keyed by CRM ID with contact data\n */\n public function getContactsByIds(array $crmIds, array $fields): array\n {\n return $this->batchReadObjects('contacts', $crmIds, $fields);\n }\n\n /**\n * @throws CompanyApiException\n * @throws CrmException\n */\n public function getAccountById(string $crmId, array $fields): array\n {\n try {\n $company = $this->getNewInstance()->crm()->companies()->basicApi()->getById(\n $crmId,\n implode(',', $fields),\n );\n } catch (CompanyApiException $e) {\n $this->log->info('[Hubspot] Failed to fetch account', [\n 'crm_id' => $crmId,\n 'reason' => $e->getMessage(),\n ]);\n\n throw $e;\n }\n\n if (! $company instanceof CompaniesWithAssociations) {\n throw new CrmException('Account not found');\n }\n\n return [\n 'id' => $company->getId(),\n 'properties' => $company->getProperties(),\n ];\n }\n\n /**\n * @throws ContactApiException\n * @throws CrmException\n */\n public function getContactById(string $crmId, array $fields): array\n {\n try {\n $contact = $this->getNewInstance()->crm()->contacts()->basicApi()->getById(\n $crmId,\n implode(',', $fields)\n );\n } catch (ContactApiException $e) {\n $this->log->info('[Hubspot] Failed to fetch contact', [\n 'crm_id' => $crmId,\n 'reason' => $e->getMessage(),\n ]);\n\n throw $e;\n }\n\n if (! $contact instanceof ContactsWithAssociations) {\n throw new CrmException('Contact not found');\n }\n\n return [\n 'id' => $contact->getId(),\n 'properties' => $contact->getProperties(),\n ];\n }\n\n /**\n * This is email search request that Hubspot offers as GET (more generous quota)\n */\n public function getContactByEmail(string $email, array $fields = []): array\n {\n try {\n $contact = $this->getNewInstance()->crm()->contacts()->basicApi()->getById(\n $email,\n implode(',', $fields),\n null,\n false,\n 'email'\n );\n\n return [\n 'id' => $contact->getId(),\n 'properties' => $contact->getProperties(),\n ];\n } catch (ContactApiException $e) {\n $this->log->info('[Hubspot] Failed to fetch contact', [\n 'email' => $email,\n 'reason' => $e->getMessage(),\n ]);\n\n return [];\n }\n }\n\n /**\n * @throws CrmException\n */\n public function fetchProperty(string $objectType, string $propertyId): Property\n {\n $result = $this->getNewInstance()->crm()->properties()->coreApi()->getByName($objectType, $propertyId);\n\n if (! $result instanceof Property) {\n $this->log->error('[Hubspot] Failed to fetch property', [\n 'object_type' => $objectType,\n 'property_id' => $propertyId,\n 'reason' => $result->getMessage(),\n ]);\n\n throw new CrmException('Failed to fetch property');\n }\n\n return $result;\n }\n\n /**\n * @return array<CrmFieldOption>\n */\n public function fetchPropertyOptions(string $objectType, string $propertyId): array\n {\n /** @var array<CrmFieldOption> */\n return $this->fetchProperty($objectType, $propertyId)->getOptions();\n }\n\n /**\n * @return array<array{id:string, label:string, deleted:bool}>\n */\n public function fetchCallDispositions(): array\n {\n /** @var Response $response */\n $response = $this->getInstance()->engagements()->getCallDispositions();\n\n /**\n * @var array<array{\n * id:string,\n * label:string,\n * deleted: bool\n * }>\n */\n return $response->toArray();\n }\n\n /**\n * @return array<CrmFieldOption>\n */\n public function fetchOpportunityPipelineStages(): array\n {\n $stages = [];\n $apiResponse = $this->getNewInstance()->crm()->pipelines()->pipelinesApi()->getAll('deals');\n\n if ($apiResponse instanceof Error) {\n $this->log->error('[Hubspot] Failed to fetch opportunity pipelines', [\n 'reason' => $apiResponse->getMessage(),\n ]);\n\n return [];\n }\n\n foreach ($apiResponse->getResults() as $pipeline) {\n $pipelineStages = array_map(\n static function (PipelineStage $stage) {\n return [\n 'id' => $stage->getId(),\n 'label' => $stage->getLabel(),\n ];\n },\n $pipeline->getStages()\n );\n\n $stages = array_merge($stages, $pipelineStages);\n }\n\n return $stages;\n }\n\n public function fetchOpportunityPipelines(): array\n {\n $pipelines = [];\n\n try {\n $apiResponse = $this->makeRequest('/crm/v3/pipelines/deals');\n } catch (\\Exception $e) {\n $this->log->info('[Hubspot] Failed to fetch opportunity pipelines', [\n 'reason' => $e->getMessage(),\n ]);\n\n return [];\n }\n\n $response = $apiResponse->toArray();\n\n foreach ($response['results'] as $pipeline) {\n $pipelines[] = [\n 'id' => $pipeline['id'],\n 'label' => $pipeline['label'],\n ];\n }\n\n return $pipelines;\n }\n\n /**\n * @return array<CrmFieldOption>\n */\n public function fetchMeetingOutcomeFieldOptions(Field $field): array\n {\n return $field->getCrmProviderId() === 'meetingOutcome'\n ? $this->fetchMeetingOutcomeTypes()\n : $this->fetchCallActivityTypes();\n }\n\n public function fetchMeetingOutcomeTypes(): array\n {\n return $this->extractMeetingTypeOptions(\n 'https://api.hubapi.com/crm/v3/properties/meeting/hs_meeting_outcome'\n );\n }\n\n public function fetchCallActivityTypes(): array\n {\n return $this->extractMeetingTypeOptions(\n 'https://api.hubapi.com/crm/v3/properties/call/hs_activity_type'\n );\n }\n\n private function extractMeetingTypeOptions(string $endpoint): array\n {\n /** @var Response $response */\n $response = $this->getInstance()\n ->getClient()\n ->request('GET', $endpoint);\n\n /**\n * @var array<array{\n * value: string,\n * label: string,\n * displayOrder: int\n * }> $optionData\n */\n $optionData = $response->toArray()['options'] ?? [];\n\n $options = [];\n foreach ($optionData as $item) {\n $options[] = [\n 'id' => $item['value'],\n 'value' => $item['value'],\n 'label' => $item['label'],\n 'display_order' => $item['displayOrder'],\n ];\n }\n\n return $options;\n }\n\n /**\n * @return array<CrmFieldOption>\n */\n public function fetchDispositionFieldOptions(): array\n {\n $options = [];\n\n $dispositions = $this->fetchCallDispositions();\n\n foreach ($dispositions as $disposition) {\n if ($disposition['deleted'] !== false) {\n continue;\n }\n\n $option['value'] = $disposition['id'];\n $option['id'] = $disposition['id'];\n $option['label'] = $disposition['label'];\n\n $options[] = $option;\n }\n\n return $options;\n }\n\n /**\n * @return array<CrmFieldOption>\n */\n public function fetchOpportunityFieldOptions(Field $field): array\n {\n if ($field->isStageField()) {\n return $this->fetchOpportunityPipelineStages();\n }\n\n if ($field->isPipelineField()) {\n return $this->fetchOpportunityPipelines();\n }\n\n return $this->fetchPropertyOptions('deals', $field->getCrmProviderId());\n }\n\n /**\n * @throws BadRequest\n * @throws HubspotException\n */\n public function makeRequest(string $endpoint, $method = 'GET', $payload = [], ?string $queryString = null)\n {\n $endpoint = self::BASE_URL . $endpoint;\n\n if ($method === 'GET') {\n return $this->getInstance()->getClient()?->request(\n method: $method,\n endpoint: $endpoint,\n query_string: $queryString\n );\n } else {\n return $this->getInstance()->getClient()->request($method, $endpoint, [\n 'json' => ($payload),\n ]);\n }\n }\n\n /**\n * @throws BadRequest\n * @throws HubspotException\n */\n public function createMeeting(array $payload): Response\n {\n $endpoint = '/crm/v3/objects/meetings';\n\n return $this->makeRequest($endpoint, 'POST', $payload);\n }\n\n /**\n * @throws BadRequest\n * @throws HubspotException\n */\n public function updateMeeting(string $meetingId, array $payload): Response\n {\n $endpoint = '/crm/v3/objects/meetings/' . $meetingId;\n\n return $this->makeRequest($endpoint, 'PATCH', $payload);\n }\n\n /**\n * @throws \\Exception\n */\n public function createNote(\n string $body,\n string $ownerId,\n int $timestamp,\n string $objectId,\n NoteObject $noteObject\n ): ?string {\n try {\n $noteInput = new SimplePublicObjectInput([\n 'properties' => [\n 'hs_note_body' => $body,\n 'hubspot_owner_id' => $ownerId,\n 'hs_timestamp' => $timestamp,\n ],\n ]);\n\n // Create note\n $note = $this->getNewInstance()->crm()->objects()->basicApi()->create('note', $noteInput);\n\n $this->getNewInstance()->crm()->objects()->associationsApi()->create(\n 'note',\n $note->getId(),\n $this->getNoteObject($noteObject),\n $objectId,\n $this->getNoteAssociationType($noteObject),\n );\n\n return $note->getId();\n } catch (\\Exception $e) {\n $this->log->error('[Hubspot] Failed to create note', [\n 'objectId' => $objectId,\n 'noteObject' => $noteObject->getObjectType(),\n 'reason' => $e->getMessage(),\n ]);\n\n \\Sentry::captureException($e);\n }\n\n return null;\n }\n\n public function updateEngagement(string $objectId, array $engagement, array $metadata): void\n {\n $this->getInstance()->engagements()->update($objectId, $engagement, $metadata);\n }\n\n public function getEngagementData(string $engagementId): array\n {\n $engagement = $this->getInstance()->engagements()->get($engagementId);\n\n return $engagement->toArray();\n }\n\n public function createEngagement(array $engagement, array $associations, array $metadata): Response\n {\n return $this->getInstance()\n ->engagements()\n ->create($engagement, $associations, $metadata);\n }\n\n public function isUnauthorizedException(\\Exception $e): bool\n {\n // Check for specific HubSpot API exception types first\n if ($e instanceof BadRequest) {\n // BadRequest can contain 401 status codes\n return $e->getCode() === 401;\n }\n\n // Check for HTTP client exceptions with status codes\n if ($e instanceof \\GuzzleHttp\\Exception\\RequestException && $e->hasResponse()) {\n $response = $e->getResponse();\n if ($response !== null) {\n return $response->getStatusCode() === 401;\n }\n }\n\n // Check for Guzzle HTTP exceptions\n if ($e instanceof \\GuzzleHttp\\Exception\\ClientException) {\n return $e->getCode() === 401;\n }\n\n // Fallback to string matching as last resort, but be more specific\n $message = strtolower($e->getMessage());\n\n return str_contains($message, '401 unauthorized') ||\n str_contains($message, 'http 401') ||\n str_contains($message, 'status code 401') ||\n (preg_match('/\\b401\\b/', $message) && str_contains($message, 'unauthorized'));\n }\n\n /**\n * Validates and refreshes the access token if needed before API requests.\n * This ensures long-running processes don't fail due to token expiration.\n *\n * @throws SocialAccountTokenInvalidException\n */\n public function ensureValidToken(): void\n {\n if ($this->oauthAccount === null) {\n return;\n }\n\n $newToken = $this->tokenManager->ensureValidToken($this->oauthAccount);\n if ($newToken !== null) {\n $this->accessToken = $newToken;\n }\n }\n\n public function getConfig()\n {\n return $this->config;\n }\n\n // returns only active (archived=false)\n public function getOwners(): array\n {\n return $this->getNewInstance()->crm()->owners()->getAll();\n }\n\n /**\n * @param bool $archived\n *\n * @return array<Owner>|[]\n */\n public function getOwnersArchived(bool $archived = true): array\n {\n $endpoint = '/crm/v3/owners';\n $queryParams = [\n 'archived' => $archived ? 'true' : 'false',\n ];\n $queryString = http_build_query($queryParams);\n\n $owners = [];\n\n try {\n $response = $this->makeRequest(endpoint: $endpoint, queryString: $queryString);\n $responseData = $response?->toArray();\n\n foreach ($responseData['results'] as $result) {\n try {\n $owners[] = Owner::create($result);\n } catch (Throwable $e) {\n $this->log->error('[HubSpot] Failed to process owner data', [\n 'result' => $result,\n 'error' => $e->getMessage(),\n ]);\n\n continue;\n }\n }\n } catch (Throwable $e) {\n $this->log->error('HubSpot] Failed to fetch owners', [\n 'archived' => $archived,\n 'error' => $e->getMessage(),\n ]);\n\n return [];\n }\n\n return $owners;\n }\n\n public function getMeeting(string $engagementId): ObjectWithAssociations\n {\n return $this->getNewInstance()->crm()->objects()->basicApi()\n ->getById('meeting', $engagementId, null, 'contact,company,deal');\n }\n\n public function deleteEngagement(string $engagementId): void\n {\n $this->getInstance()->engagements()->delete((int) $engagementId);\n }\n\n public function getAssociationsData(array $ids, string $fromObject, string $toObject): array\n {\n $associationData = [];\n $idChunks = array_chunk($ids, self::ASSOCIATIONS_BATCH_SIZE_LIMIT);\n\n foreach ($idChunks as $idChunk) {\n try {\n $batchInput = new \\HubSpot\\Client\\Crm\\Associations\\Model\\BatchInputPublicObjectId();\n $batchInput->setInputs(array_map(function ($id) {\n $publicObjectId = new \\HubSpot\\Client\\Crm\\Associations\\Model\\PublicObjectId();\n $publicObjectId->setId($id);\n\n return $publicObjectId;\n }, $idChunk));\n\n $associatedObjectsData = $this\n ->getNewInstance()\n ->crm()\n ->associations()\n ->batchApi()\n ->read($fromObject, $toObject, $batchInput);\n\n if ($associatedObjectsData instanceof \\HubSpot\\Client\\Crm\\Associations\\Model\\BatchResponsePublicAssociationMulti) {\n foreach ($associatedObjectsData->getResults() as $association) {\n $from = $association->getFrom()->getId();\n $toAssociations = $association->getTo();\n\n if (! empty($toAssociations)) {\n $associationData[$from] = array_map(function ($item) {\n return $item->getId();\n }, $toAssociations);\n }\n }\n }\n// } catch (RateLimitException $e) {\n// throw $e;\n } catch (\\Exception $e) {\n $this->log->error('[Hubspot] Failed to fetch associations', [\n 'from_object' => $fromObject,\n 'to_object' => $toObject,\n 'reason' => $e->getMessage(),\n ]);\n }\n }\n\n return $associationData;\n }\n\n /**\n * @throws \\Exception\n */\n private function getNoteAssociationType(NoteObject $noteObject): string\n {\n return match($noteObject) {\n NoteObject::Opportunity => 'note_to_deal',\n NoteObject::Lead, NoteObject::Contact => 'note_to_contact', // or 'note_to_lead' if your portal supports it\n NoteObject::Account => 'note_to_company',\n NoteObject::Call, NoteObject::Event => throw new \\Exception('Not supported'),\n };\n }\n\n /**\n * @throws \\Exception\n */\n private function getNoteObject(NoteObject $noteObject): string\n {\n return match($noteObject) {\n NoteObject::Opportunity => 'deal',\n NoteObject::Lead, NoteObject::Contact => 'contact',\n NoteObject::Account => 'company',\n NoteObject::Call, NoteObject::Event => throw new \\Exception('Not supported'),\n };\n }\n\n public function addAssociations(string $objectType, string $associationType, array $payload): Response\n {\n $endpoint = \"/crm/v4/associations/$objectType/$associationType/batch/create\";\n\n return $this->makeRequest($endpoint, 'POST', $payload);\n }\n\n public function removeAssociations(string $objectType, string $associationType, array $payload): Response\n {\n $endpoint = \"/crm/v4/associations/$objectType/$associationType/batch/archive\";\n\n return $this->makeRequest($endpoint, 'POST', $payload);\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":true,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"19","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
8811914002027558382
|
6378759348859832420
|
click
|
accessibility
|
NULL
|
Project: faVsco.js, menu
JY-20725-handle-HS-search Project: faVsco.js, menu
JY-20725-handle-HS-search-rate-limit, menu
Start Listening for PHP Debug Connections
HandleHubspotRateLimitTest
Run 'HandleHubspotRateLimitTest'
Debug 'HandleHubspotRateLimitTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
2
67
3
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Services\Crm\Hubspot;
use HubSpot\Client\Crm\Deals\ApiException as DealApiException;
use HubSpot\Client\Crm\Contacts\ApiException as ContactApiException;
use HubSpot\Client\Crm\Companies\ApiException as CompanyApiException;
use HubSpot\Client\Crm\Contacts\Model\SimplePublicObjectWithAssociations as ContactsWithAssociations;
use HubSpot\Client\Crm\Companies\Model\SimplePublicObjectWithAssociations as CompaniesWithAssociations;
use HubSpot\Client\Crm\Deals\Model\SimplePublicObjectWithAssociations as DealWithAssociations;
use HubSpot\Client\Crm\Objects\Model\SimplePublicObjectInput;
use HubSpot\Client\Crm\Objects\Model\SimplePublicObjectWithAssociations as ObjectWithAssociations;
use HubSpot\Client\Crm\Pipelines\Model\Error;
use HubSpot\Client\Crm\Pipelines\Model\PipelineStage;
use HubSpot\Client\Crm\Properties\Model\Property;
use HubSpot\Discovery\Discovery;
use Jiminny\Exceptions\CrmException;
use Jiminny\Exceptions\RateLimitException;
use Jiminny\Exceptions\SocialAccountTokenInvalidException;
use Jiminny\Jobs\Crm\NoteObject;
use Jiminny\Models\Crm\Field;
use Jiminny\Services\Crm\BaseClient;
use Jiminny\Services\Crm\Hubspot\DTO\Response\Owner;
use Jiminny\Services\SocialAccountService;
use SevenShores\Hubspot\Exceptions\BadRequest;
use SevenShores\Hubspot\Exceptions\HubspotException;
use SevenShores\Hubspot\Factory;
use SevenShores\Hubspot\Http\Response;
use Jiminny\Services\Crm\Hubspot\Pagination\HubspotPaginationService;
use Illuminate\Support\Facades\Redis;
use Throwable;
/**
* @phpstan-type CrmFieldOption array{id:string, label:string, value?:string}
*/
class Client extends BaseClient implements HubspotClientInterface
{
public const string MIN_API_VERSION = '2';
public const string BASE_URL = '[URL_WITH_CREDENTIALS] T
* @param callable(): T $apiCall
* @return T
*
* @throws RateLimitException
*/
private function executeRequest(callable $apiCall)
{
$cacheKey = $this->getRateLimitCacheKey();
$cachedRetryAfter = Redis::get($cacheKey);
if (is_string($cachedRetryAfter) && is_numeric($cachedRetryAfter)) {
throw new RateLimitException(
'Hubspot rate limit (cached circuit-breaker)',
(int) $cachedRetryAfter,
);
}
try {
return $apiCall();
} catch (Throwable $e) {
if ($this->isHubspotRateLimit($e)) {
$retryAfter = $this->parseRetryAfter($e);
Redis::setex($cacheKey, $retryAfter, (string) $retryAfter);
$this->log->warning('[Hubspot] Received 429 from API', [
'team_id' => $this->config->team_id,
'config_id' => $this->config->getId(),
'retry_after' => $retryAfter,
'message' => $this->parseMessage($e),
'reason' => $e->getMessage(),
]);
throw new RateLimitException('Hubspot returned 429', $retryAfter, $e);
}
throw $e;
}
}
private function getRateLimitCacheKey(): string
{
return sprintf('hubspot:ratelimit:portal:%d', $this->config->getId());
}
public function isHubspotRateLimit(Throwable $e): bool
{
if ($e instanceof BadRequest
|| $e instanceof DealApiException
|| $e instanceof ContactApiException
|| $e instanceof CompanyApiException
|| $e instanceof \GuzzleHttp\Exception\RequestException
) {
return (int) $e->getCode() === 429;
}
return false;
}
public function parseRetryAfter(Throwable $e): int
{
if (method_exists($e, 'getResponseHeaders')) {
$headers = $e->getResponseHeaders() ?: [];
$value = $headers['Retry-After'] ?? $headers['retry-after'] ?? null;
if (is_array($value)) {
$value = $value[0] ?? null;
}
if (is_numeric($value)) {
return (int) $value;
}
}
$message = strtolower($this->parseMessage($e));
if (str_contains($message, 'daily')) {
return 600;
}
if (str_contains($message, 'ten secondly')) {
return 10;
}
if (str_contains($message, 'secondly')) {
return 1;
}
$this->log->warning('[Hubspot] No retry-after header or known message, using default', [
'exception_class' => get_class($e),
'message' => $message,
]);
return 10;
}
public function parseMessage(Throwable $e): string
{
if (! method_exists($e, 'getResponseBody')) {
return '';
}
$body = $e->getResponseBody();
if (is_string($body)) {
$body = json_decode($body, true) ?? [];
}
if (! is_array($body)) {
return '';
}
return (string) ($body['message'] ?? '');
}
public function getMinimumApiVersion(): string
{
return self::MIN_API_VERSION;
}
public function getInstance(): Factory
{
return new Factory([
'key' => $this->accessToken,
'oauth2' => true,
'base_url' => $this->baseUrl,
]);
}
public function getNewInstance(): Discovery
{
return \HubSpot\Factory::createWithAccessToken($this->accessToken);
}
/**
* Secondly and daily limits for Hubspot API
*
* Product Tier: Free & Starter | Professional & Enterprise | API add-on (any tier)
* Burst: 100/10 seconds | 150/10 seconds | 200/10 seconds
* Daily: 250,000 | 500,000 | 1,000,000
*
* Official documentation states: The search endpoints are rate limited to five requests per second.
* Since with 5 RPS were still hitting secondly rate limits we lowered it to 4
*/
public function getPaginatedData(array $payload, string $type, int $offset = 0): array
{
$total = 0;
$lastId = null;
$rows = [];
foreach ($this->getPaginatedDataGenerator($payload, $type, $offset, $total, $lastId) as $row) {
$rows[] = $row;
}
return ['results' => $rows, 'total' => $total, 'last_record' => $lastId];
}
/**
* @throws HubspotException
* @throws SocialAccountTokenInvalidException
* @throws BadRequest
*/
public function getPaginatedDataGenerator(
array $payload,
string $type,
int $offset = 0,
int &$total = 0,
?string &$lastRecordId = null
): \Generator {
return $this->paginationService->getPaginatedDataGenerator(
$this,
$payload,
$type,
$offset,
$total,
$lastRecordId
);
}
/**
* Execute a search request against HubSpot CRM objects with rate limiting.
*
* @param string $objectType The object type ('deals', 'companies', 'contacts', 'calls')
* @param array<string, mixed> $payload The search payload with filters, sorts, properties, etc.
* @return array The search response with 'results', 'total', 'paging' keys
* @throws RateLimitException When rate limit is hit
* @throws HubspotException On API errors
*/
public function search(string $objectType, array $payload): array
{
$endpoint = self::BASE_URL . "/crm/v3/objects/{$objectType}/search";
return $this->executeRequest(function () use ($endpoint, $payload) {
$response = $this->getInstance()->getClient()->request('POST', $endpoint, ['json' => $payload]);
return $response->toArray();
});
}
/**
* @throws DealApiException
* @throws CrmException
*/
public function getOpportunityById(string $crmId, array $fields): array
{
try {
// $deal = $this->executeRequest(fn () => $this->getNewInstance()->crm()->deals()->basicApi()->getById(
$deal = $this->getNewInstance()->crm()->deals()->basicApi()->getById(
$crmId,
implode(',', $fields),
'companies,contacts'
);
} catch (DealApiException $e) {
$this->log->info('[Hubspot] Failed to fetch opportunity', [
'crm_id' => $crmId,
'reason' => $e->getMessage(),
]);
throw $e;
}
if (! $deal instanceof DealWithAssociations) {
throw new CrmException('Deal not found');
}
return [
'id' => $deal->getId(),
'properties' => $deal->getProperties(),
'associations' => $deal->getAssociations(),
];
}
/**
* Generic batch read method for HubSpot objects
*
* @param string $objectType The object type ('deals', 'companies', 'contacts')
* @param array<string> $crmIds Array of HubSpot object IDs (max 100)
* @param array<string> $fields Array of property names to fetch
*
* @return array<string, array> Array keyed by CRM ID with object data
*/
private function batchReadObjects(string $objectType, array $crmIds, array $fields): array
{
if (empty($crmIds)) {
return [];
}
$this->validateBatchSize($objectType, $crmIds);
$this->ensureValidToken();
try {
$batchConfig = $this->createBatchConfiguration($objectType);
$batchReadRequest = $this->prepareBatchRequest($batchConfig, $crmIds, $fields);
$response = $batchConfig['api']->read($batchReadRequest);
$this->validateApiResponse($response, $objectType);
$results = $this->processApiResults($response);
$this->logBatchResults($objectType, $crmIds, $results);
return $results;
} catch (\Throwable $e) {
$this->handleBatchError($e, $objectType, $crmIds);
}
}
private function validateBatchSize(string $objectType, array $crmIds): void
{
if (count($crmIds) > 100) {
throw new \InvalidArgumentException("Batch size cannot exceed 100 {$objectType}");
}
}
private function createBatchConfiguration(string $objectType): array
{
$configurations = [
'deals' => [
'batchReadRequest' => new \HubSpot\Client\Crm\Deals\Model\BatchReadInputSimplePublicObjectId(),
'inputClass' => \HubSpot\Client\Crm\Deals\Model\SimplePublicObjectId::class,
'api' => $this->getNewInstance()->crm()->deals()->batchApi(),
],
'companies' => [
'batchReadRequest' => new \HubSpot\Client\Crm\Companies\Model\BatchReadInputSimplePublicObjectId(),
'inputClass' => \HubSpot\Client\Crm\Companies\Model\SimplePublicObjectId::class,
'api' => $this->getNewInstance()->crm()->companies()->batchApi(),
],
'contacts' => [
'batchReadRequest' => new \HubSpot\Client\Crm\Contacts\Model\BatchReadInputSimplePublicObjectId(),
'inputClass' => \HubSpot\Client\Crm\Contacts\Model\SimplePublicObjectId::class,
'api' => $this->getNewInstance()->crm()->contacts()->batchApi(),
],
];
if (! isset($configurations[$objectType])) {
throw new \InvalidArgumentException("Unsupported object type: {$objectType}");
}
return $configurations[$objectType];
}
private function prepareBatchRequest(array $batchConfig, array $crmIds, array $fields): object
{
$batchReadRequest = $batchConfig['batchReadRequest'];
$inputClass = $batchConfig['inputClass'];
$inputs = array_map(function ($crmId) use ($inputClass) {
$input = new $inputClass();
$input->setId($crmId);
return $input;
}, $crmIds);
$batchReadRequest->setInputs($inputs);
$batchReadRequest->setProperties($fields);
return $batchReadRequest;
}
private function validateApiResponse($response, string $objectType): void
{
if (! $response) {
throw new CrmException("HubSpot API returned null response for {$objectType} batch read");
}
}
private function processApiResults($response): array
{
$results = [];
$responseResults = $response->getResults();
if ($responseResults) {
foreach ($responseResults as $object) {
if ($object && $object->getId()) {
$results[$object->getId()] = [
'id' => $object->getId(),
'properties' => $object->getProperties() ?: [],
];
}
}
}
return $results;
}
private function logBatchResults(string $objectType, array $crmIds, array $results): void
{
$this->log->info("[HubSpot] Batch fetched {$objectType}", [
'requested_count' => count($crmIds),
'returned_count' => count($results),
'crm_ids' => $crmIds,
]);
}
private function handleBatchError(\Throwable $e, string $objectType, array $crmIds): void
{
$errorMessage = $e->getMessage() ?: 'Unknown error';
$errorTrace = $e->getTraceAsString() ?: 'No trace available';
$this->log->error("[HubSpot] Failed to batch fetch {$objectType}", [
'crm_ids' => $crmIds,
'error' => $errorMessage,
'trace' => $errorTrace,
]);
throw new CrmException("Failed to batch fetch {$objectType}: " . $errorMessage);
}
/**
* Batch read multiple opportunities by their CRM IDs
*
* @param array<string> $crmIds Array of HubSpot deal IDs (max 100)
* @param array<string> $fields Array of property names to fetch
*
* @return array<string, array> Array keyed by CRM ID with opportunity data
*/
public function getOpportunitiesByIds(array $crmIds, array $fields): array
{
return $this->batchReadObjects('deals', $crmIds, $fields);
}
/**
* Batch read multiple companies by their CRM IDs
*
* @param array<string> $crmIds Array of HubSpot company IDs (max 100)
* @param array<string> $fields Array of property names to fetch
*
* @return array<string, array> Array keyed by CRM ID with company data
*/
public function getCompaniesByIds(array $crmIds, array $fields): array
{
return $this->batchReadObjects('companies', $crmIds, $fields);
}
/**
* Batch read multiple contacts by their CRM IDs
*
* @param array<string> $crmIds Array of HubSpot contact IDs (max 100)
* @param array<string> $fields Array of property names to fetch
*
* @return array<string, array> Array keyed by CRM ID with contact data
*/
public function getContactsByIds(array $crmIds, array $fields): array
{
return $this->batchReadObjects('contacts', $crmIds, $fields);
}
/**
* @throws CompanyApiException
* @throws CrmException
*/
public function getAccountById(string $crmId, array $fields): array
{
try {
$company = $this->getNewInstance()->crm()->companies()->basicApi()->getById(
$crmId,
implode(',', $fields),
);
} catch (CompanyApiException $e) {
$this->log->info('[Hubspot] Failed to fetch account', [
'crm_id' => $crmId,
'reason' => $e->getMessage(),
]);
throw $e;
}
if (! $company instanceof CompaniesWithAssociations) {
throw new CrmException('Account not found');
}
return [
'id' => $company->getId(),
'properties' => $company->getProperties(),
];
}
/**
* @throws ContactApiException
* @throws CrmException
*/
public function getContactById(string $crmId, array $fields): array
{
try {
$contact = $this->getNewInstance()->crm()->contacts()->basicApi()->getById(
$crmId,
implode(',', $fields)
);
} catch (ContactApiException $e) {
$this->log->info('[Hubspot] Failed to fetch contact', [
'crm_id' => $crmId,
'reason' => $e->getMessage(),
]);
throw $e;
}
if (! $contact instanceof ContactsWithAssociations) {
throw new CrmException('Contact not found');
}
return [
'id' => $contact->getId(),
'properties' => $contact->getProperties(),
];
}
/**
* This is email search request that Hubspot offers as GET (more generous quota)
*/
public function getContactByEmail(string $email, array $fields = []): array
{
try {
$contact = $this->getNewInstance()->crm()->contacts()->basicApi()->getById(
$email,
implode(',', $fields),
null,
false,
'email'
);
return [
'id' => $contact->getId(),
'properties' => $contact->getProperties(),
];
} catch (ContactApiException $e) {
$this->log->info('[Hubspot] Failed to fetch contact', [
'email' => $email,
'reason' => $e->getMessage(),
]);
return [];
}
}
/**
* @throws CrmException
*/
public function fetchProperty(string $objectType, string $propertyId): Property
{
$result = $this->getNewInstance()->crm()->properties()->coreApi()->getByName($objectType, $propertyId);
if (! $result instanceof Property) {
$this->log->error('[Hubspot] Failed to fetch property', [
'object_type' => $objectType,
'property_id' => $propertyId,
'reason' => $result->getMessage(),
]);
throw new CrmException('Failed to fetch property');
}
return $result;
}
/**
* @return array<CrmFieldOption>
*/
public function fetchPropertyOptions(string $objectType, string $propertyId): array
{
/** @var array<CrmFieldOption> */
return $this->fetchProperty($objectType, $propertyId)->getOptions();
}
/**
* @return array<array{id:string, label:string, deleted:bool}>
*/
public function fetchCallDispositions(): array
{
/** @var Response $response */
$response = $this->getInstance()->engagements()->getCallDispositions();
/**
* @var array<array{
* id:string,
* label:string,
* deleted: bool
* }>
*/
return $response->toArray();
}
/**
* @return array<CrmFieldOption>
*/
public function fetchOpportunityPipelineStages(): array
{
$stages = [];
$apiResponse = $this->getNewInstance()->crm()->pipelines()->pipelinesApi()->getAll('deals');
if ($apiResponse instanceof Error) {
$this->log->error('[Hubspot] Failed to fetch opportunity pipelines', [
'reason' => $apiResponse->getMessage(),
]);
return [];
}
foreach ($apiResponse->getResults() as $pipeline) {
$pipelineStages = array_map(
static function (PipelineStage $stage) {
return [
'id' => $stage->getId(),
'label' => $stage->getLabel(),
];
},
$pipeline->getStages()
);
$stages = array_merge($stages, $pipelineStages);
}
return $stages;
}
public function fetchOpportunityPipelines(): array
{
$pipelines = [];
try {
$apiResponse = $this->makeRequest('/crm/v3/pipelines/deals');
} catch (\Exception $e) {
$this->log->info('[Hubspot] Failed to fetch opportunity pipelines', [
'reason' => $e->getMessage(),
]);
return [];
}
$response = $apiResponse->toArray();
foreach ($response['results'] as $pipeline) {
$pipelines[] = [
'id' => $pipeline['id'],
'label' => $pipeline['label'],
];
}
return $pipelines;
}
/**
* @return array<CrmFieldOption>
*/
public function fetchMeetingOutcomeFieldOptions(Field $field): array
{
return $field->getCrmProviderId() === 'meetingOutcome'
? $this->fetchMeetingOutcomeTypes()
: $this->fetchCallActivityTypes();
}
public function fetchMeetingOutcomeTypes(): array
{
return $this->extractMeetingTypeOptions(
'[URL_WITH_CREDENTIALS] Response $response */
$response = $this->getInstance()
->getClient()
->request('GET', $endpoint);
/**
* @var array<array{
* value: string,
* label: string,
* displayOrder: int
* }> $optionData
*/
$optionData = $response->toArray()['options'] ?? [];
$options = [];
foreach ($optionData as $item) {
$options[] = [
'id' => $item['value'],
'value' => $item['value'],
'label' => $item['label'],
'display_order' => $item['displayOrder'],
];
}
return $options;
}
/**
* @return array<CrmFieldOption>
*/
public function fetchDispositionFieldOptions(): array
{
$options = [];
$dispositions = $this->fetchCallDispositions();
foreach ($dispositions as $disposition) {
if ($disposition['deleted'] !== false) {
continue;
}
$option['value'] = $disposition['id'];
$option['id'] = $disposition['id'];
$option['label'] = $disposition['label'];
$options[] = $option;
}
return $options;
}
/**
* @return array<CrmFieldOption>
*/
public function fetchOpportunityFieldOptions(Field $field): array
{
if ($field->isStageField()) {
return $this->fetchOpportunityPipelineStages();
}
if ($field->isPipelineField()) {
return $this->fetchOpportunityPipelines();
}
return $this->fetchPropertyOptions('deals', $field->getCrmProviderId());
}
/**
* @throws BadRequest
* @throws HubspotException
*/
public function makeRequest(string $endpoint, $method = 'GET', $payload = [], ?string $queryString = null)
{
$endpoint = self::BASE_URL . $endpoint;
if ($method === 'GET') {
return $this->getInstance()->getClient()?->request(
method: $method,
endpoint: $endpoint,
query_string: $queryString
);
} else {
return $this->getInstance()->getClient()->request($method, $endpoint, [
'json' => ($payload),
]);
}
}
/**
* @throws BadRequest
* @throws HubspotException
*/
public function createMeeting(array $payload): Response
{
$endpoint = '/crm/v3/objects/meetings';
return $this->makeRequest($endpoint, 'POST', $payload);
}
/**
* @throws BadRequest
* @throws HubspotException
*/
public function updateMeeting(string $meetingId, array $payload): Response
{
$endpoint = '/crm/v3/objects/meetings/' . $meetingId;
return $this->makeRequest($endpoint, 'PATCH', $payload);
}
/**
* @throws \Exception
*/
public function createNote(
string $body,
string $ownerId,
int $timestamp,
string $objectId,
NoteObject $noteObject
): ?string {
try {
$noteInput = new SimplePublicObjectInput([
'properties' => [
'hs_note_body' => $body,
'hubspot_owner_id' => $ownerId,
'hs_timestamp' => $timestamp,
],
]);
// Create note
$note = $this->getNewInstance()->crm()->objects()->basicApi()->create('note', $noteInput);
$this->getNewInstance()->crm()->objects()->associationsApi()->create(
'note',
$note->getId(),
$this->getNoteObject($noteObject),
$objectId,
$this->getNoteAssociationType($noteObject),
);
return $note->getId();
} catch (\Exception $e) {
$this->log->error('[Hubspot] Failed to create note', [
'objectId' => $objectId,
'noteObject' => $noteObject->getObjectType(),
'reason' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
return null;
}
public function updateEngagement(string $objectId, array $engagement, array $metadata): void
{
$this->getInstance()->engagements()->update($objectId, $engagement, $metadata);
}
public function getEngagementData(string $engagementId): array
{
$engagement = $this->getInstance()->engagements()->get($engagementId);
return $engagement->toArray();
}
public function createEngagement(array $engagement, array $associations, array $metadata): Response
{
return $this->getInstance()
->engagements()
->create($engagement, $associations, $metadata);
}
public function isUnauthorizedException(\Exception $e): bool
{
// Check for specific HubSpot API exception types first
if ($e instanceof BadRequest) {
// BadRequest can contain 401 status codes
return $e->getCode() === 401;
}
// Check for HTTP client exceptions with status codes
if ($e instanceof \GuzzleHttp\Exception\RequestException && $e->hasResponse()) {
$response = $e->getResponse();
if ($response !== null) {
return $response->getStatusCode() === 401;
}
}
// Check for Guzzle HTTP exceptions
if ($e instanceof \GuzzleHttp\Exception\ClientException) {
return $e->getCode() === 401;
}
// Fallback to string matching as last resort, but be more specific
$message = strtolower($e->getMessage());
return str_contains($message, '401 unauthorized') ||
str_contains($message, 'http 401') ||
str_contains($message, 'status code 401') ||
(preg_match('/\b401\b/', $message) && str_contains($message, 'unauthorized'));
}
/**
* Validates and refreshes the access token if needed before API requests.
* This ensures long-running processes don't fail due to token expiration.
*
* @throws SocialAccountTokenInvalidException
*/
public function ensureValidToken(): void
{
if ($this->oauthAccount === null) {
return;
}
$newToken = $this->tokenManager->ensureValidToken($this->oauthAccount);
if ($newToken !== null) {
$this->accessToken = $newToken;
}
}
public function getConfig()
{
return $this->config;
}
// returns only active (archived=false)
public function getOwners(): array
{
return $this->getNewInstance()->crm()->owners()->getAll();
}
/**
* @param bool $archived
*
* @return array<Owner>|[]
*/
public function getOwnersArchived(bool $archived = true): array
{
$endpoint = '/crm/v3/owners';
$queryParams = [
'archived' => $archived ? 'true' : 'false',
];
$queryString = http_build_query($queryParams);
$owners = [];
try {
$response = $this->makeRequest(endpoint: $endpoint, queryString: $queryString);
$responseData = $response?->toArray();
foreach ($responseData['results'] as $result) {
try {
$owners[] = Owner::create($result);
} catch (Throwable $e) {
$this->log->error('[HubSpot] Failed to process owner data', [
'result' => $result,
'error' => $e->getMessage(),
]);
continue;
}
}
} catch (Throwable $e) {
$this->log->error('HubSpot] Failed to fetch owners', [
'archived' => $archived,
'error' => $e->getMessage(),
]);
return [];
}
return $owners;
}
public function getMeeting(string $engagementId): ObjectWithAssociations
{
return $this->getNewInstance()->crm()->objects()->basicApi()
->getById('meeting', $engagementId, null, 'contact,company,deal');
}
public function deleteEngagement(string $engagementId): void
{
$this->getInstance()->engagements()->delete((int) $engagementId);
}
public function getAssociationsData(array $ids, string $fromObject, string $toObject): array
{
$associationData = [];
$idChunks = array_chunk($ids, self::ASSOCIATIONS_BATCH_SIZE_LIMIT);
foreach ($idChunks as $idChunk) {
try {
$batchInput = new \HubSpot\Client\Crm\Associations\Model\BatchInputPublicObjectId();
$batchInput->setInputs(array_map(function ($id) {
$publicObjectId = new \HubSpot\Client\Crm\Associations\Model\PublicObjectId();
$publicObjectId->setId($id);
return $publicObjectId;
}, $idChunk));
$associatedObjectsData = $this
->getNewInstance()
->crm()
->associations()
->batchApi()
->read($fromObject, $toObject, $batchInput);
if ($associatedObjectsData instanceof \HubSpot\Client\Crm\Associations\Model\BatchResponsePublicAssociationMulti) {
foreach ($associatedObjectsData->getResults() as $association) {
$from = $association->getFrom()->getId();
$toAssociations = $association->getTo();
if (! empty($toAssociations)) {
$associationData[$from] = array_map(function ($item) {
return $item->getId();
}, $toAssociations);
}
}
}
// } catch (RateLimitException $e) {
// throw $e;
} catch (\Exception $e) {
$this->log->error('[Hubspot] Failed to fetch associations', [
'from_object' => $fromObject,
'to_object' => $toObject,
'reason' => $e->getMessage(),
]);
}
}
return $associationData;
}
/**
* @throws \Exception
*/
private function getNoteAssociationType(NoteObject $noteObject): string
{
return match($noteObject) {
NoteObject::Opportunity => 'note_to_deal',
NoteObject::Lead, NoteObject::Contact => 'note_to_contact', // or 'note_to_lead' if your portal supports it
NoteObject::Account => 'note_to_company',
NoteObject::Call, NoteObject::Event => throw new \Exception('Not supported'),
};
}
/**
* @throws \Exception
*/
private function getNoteObject(NoteObject $noteObject): string
{
return match($noteObject) {
NoteObject::Opportunity => 'deal',
NoteObject::Lead, NoteObject::Contact => 'contact',
NoteObject::Account => 'company',
NoteObject::Call, NoteObject::Event => throw new \Exception('Not supported'),
};
}
public function addAssociations(string $objectType, string $associationType, array $payload): Response
{
$endpoint = "/crm/v4/associations/$objectType/$associationType/batch/create";
return $this->makeRequest($endpoint, 'POST', $payload);
}
public function removeAssociations(string $objectType, string $associationType, array $payload): Response
{
$endpoint = "/crm/v4/associations/$objectType/$associationType/batch/archive";
return $this->makeRequest($endpoint, 'POST', $payload);
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
19
Previous Highlighted Error...
|
18191
|
NULL
|
NULL
|
NULL
|
|
18194
|
791
|
2
|
2026-05-11T11:03:41.666786+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-11/1778 /Users/lukas/.screenpipe/data/data/2026-05-11/1778497421666_m2.jpg...
|
PhpStorm
|
faVsco.js – Client.php
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
JY-20725-handle-HS-search Project: faVsco.js, menu
JY-20725-handle-HS-search-rate-limit, menu
Start Listening for PHP Debug Connections
HandleHubspotRateLimitTest
Run 'HandleHubspotRateLimitTest'
Debug 'HandleHubspotRateLimitTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
2
67
3
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Services\Crm\Hubspot;
use HubSpot\Client\Crm\Deals\ApiException as DealApiException;
use HubSpot\Client\Crm\Contacts\ApiException as ContactApiException;
use HubSpot\Client\Crm\Companies\ApiException as CompanyApiException;
use HubSpot\Client\Crm\Contacts\Model\SimplePublicObjectWithAssociations as ContactsWithAssociations;
use HubSpot\Client\Crm\Companies\Model\SimplePublicObjectWithAssociations as CompaniesWithAssociations;
use HubSpot\Client\Crm\Deals\Model\SimplePublicObjectWithAssociations as DealWithAssociations;
use HubSpot\Client\Crm\Objects\Model\SimplePublicObjectInput;
use HubSpot\Client\Crm\Objects\Model\SimplePublicObjectWithAssociations as ObjectWithAssociations;
use HubSpot\Client\Crm\Pipelines\Model\Error;
use HubSpot\Client\Crm\Pipelines\Model\PipelineStage;
use HubSpot\Client\Crm\Properties\Model\Property;
use HubSpot\Discovery\Discovery;
use Jiminny\Exceptions\CrmException;
use Jiminny\Exceptions\RateLimitException;
use Jiminny\Exceptions\SocialAccountTokenInvalidException;
use Jiminny\Jobs\Crm\NoteObject;
use Jiminny\Models\Crm\Field;
use Jiminny\Services\Crm\BaseClient;
use Jiminny\Services\Crm\Hubspot\DTO\Response\Owner;
use Jiminny\Services\SocialAccountService;
use SevenShores\Hubspot\Exceptions\BadRequest;
use SevenShores\Hubspot\Exceptions\HubspotException;
use SevenShores\Hubspot\Factory;
use SevenShores\Hubspot\Http\Response;
use Jiminny\Services\Crm\Hubspot\Pagination\HubspotPaginationService;
use Illuminate\Support\Facades\Redis;
use Throwable;
/**
* @phpstan-type CrmFieldOption array{id:string, label:string, value?:string}
*/
class Client extends BaseClient implements HubspotClientInterface
{
public const string MIN_API_VERSION = '2';
public const string BASE_URL = '[URL_WITH_CREDENTIALS] T
* @param callable(): T $apiCall
* @return T
*
* @throws RateLimitException
*/
private function executeRequest(callable $apiCall)
{
$cacheKey = $this->getRateLimitCacheKey();
$cachedRetryAfter = Redis::get($cacheKey);
if (is_string($cachedRetryAfter) && is_numeric($cachedRetryAfter)) {
throw new RateLimitException(
'Hubspot rate limit (cached circuit-breaker)',
(int) $cachedRetryAfter,
);
}
try {
return $apiCall();
} catch (Throwable $e) {
if ($this->isHubspotRateLimit($e)) {
$retryAfter = $this->parseRetryAfter($e);
Redis::setex($cacheKey, $retryAfter, (string) $retryAfter);
$this->log->warning('[Hubspot] Received 429 from API', [
'team_id' => $this->config->team_id,
'config_id' => $this->config->getId(),
'retry_after' => $retryAfter,
'message' => $this->parseMessage($e),
'reason' => $e->getMessage(),
]);
throw new RateLimitException('Hubspot returned 429', $retryAfter, $e);
}
throw $e;
}
}
private function getRateLimitCacheKey(): string
{
return sprintf('hubspot:ratelimit:portal:%d', $this->config->getId());
}
public function isHubspotRateLimit(Throwable $e): bool
{
if ($e instanceof BadRequest
|| $e instanceof DealApiException
|| $e instanceof ContactApiException
|| $e instanceof CompanyApiException
|| $e instanceof \GuzzleHttp\Exception\RequestException
) {
return (int) $e->getCode() === 429;
}
return false;
}
public function parseRetryAfter(Throwable $e): int
{
if (method_exists($e, 'getResponseHeaders')) {
$headers = $e->getResponseHeaders() ?: [];
$value = $headers['Retry-After'] ?? $headers['retry-after'] ?? null;
if (is_array($value)) {
$value = $value[0] ?? null;
}
if (is_numeric($value)) {
return (int) $value;
}
}
$message = strtolower($this->parseMessage($e));
if (str_contains($message, 'daily')) {
return 600;
}
if (str_contains($message, 'ten secondly')) {
return 10;
}
if (str_contains($message, 'secondly')) {
return 1;
}
$this->log->warning('[Hubspot] No retry-after header or known message, using default', [
'exception_class' => get_class($e),
'message' => $message,
]);
return 10;
}
public function parseMessage(Throwable $e): string
{
if (! method_exists($e, 'getResponseBody')) {
return '';
}
$body = $e->getResponseBody();
if (is_string($body)) {
$body = json_decode($body, true) ?? [];
}
if (! is_array($body)) {
return '';
}
return (string) ($body['message'] ?? '');
}
public function getMinimumApiVersion(): string
{
return self::MIN_API_VERSION;
}
public function getInstance(): Factory
{
return new Factory([
'key' => $this->accessToken,
'oauth2' => true,
'base_url' => $this->baseUrl,
]);
}
public function getNewInstance(): Discovery
{
return \HubSpot\Factory::createWithAccessToken($this->accessToken);
}
/**
* Secondly and daily limits for Hubspot API
*
* Product Tier: Free & Starter | Professional & Enterprise | API add-on (any tier)
* Burst: 100/10 seconds | 150/10 seconds | 200/10 seconds
* Daily: 250,000 | 500,000 | 1,000,000
*
* Official documentation states: The search endpoints are rate limited to five requests per second.
* Since with 5 RPS were still hitting secondly rate limits we lowered it to 4
*/
public function getPaginatedData(array $payload, string $type, int $offset = 0): array
{
$total = 0;
$lastId = null;
$rows = [];
foreach ($this->getPaginatedDataGenerator($payload, $type, $offset, $total, $lastId) as $row) {
$rows[] = $row;
}
return ['results' => $rows, 'total' => $total, 'last_record' => $lastId];
}
/**
* @throws HubspotException
* @throws SocialAccountTokenInvalidException
* @throws BadRequest
*/
public function getPaginatedDataGenerator(
array $payload,
string $type,
int $offset = 0,
int &$total = 0,
?string &$lastRecordId = null
): \Generator {
return $this->paginationService->getPaginatedDataGenerator(
$this,
$payload,
$type,
$offset,
$total,
$lastRecordId
);
}
/**
* Execute a search request against HubSpot CRM objects with rate limiting.
*
* @param string $objectType The object type ('deals', 'companies', 'contacts', 'calls')
* @param array<string, mixed> $payload The search payload with filters, sorts, properties, etc.
* @return array The search response with 'results', 'total', 'paging' keys
* @throws RateLimitException When rate limit is hit
* @throws HubspotException On API errors
*/
public function search(string $objectType, array $payload): array
{
$endpoint = self::BASE_URL . "/crm/v3/objects/{$objectType}/search";
return $this->executeRequest(function () use ($endpoint, $payload) {
$response = $this->getInstance()->getClient()->request('POST', $endpoint, ['json' => $payload]);
return $response->toArray();
});
}
/**
* @throws DealApiException
* @throws CrmException
*/
public function getOpportunityById(string $crmId, array $fields): array
{
try {
// $deal = $this->executeRequest(fn () => $this->getNewInstance()->crm()->deals()->basicApi()->getById(
$deal = $this->getNewInstance()->crm()->deals()->basicApi()->getById(
$crmId,
implode(',', $fields),
'companies,contacts'
);
} catch (DealApiException $e) {
$this->log->info('[Hubspot] Failed to fetch opportunity', [
'crm_id' => $crmId,
'reason' => $e->getMessage(),
]);
throw $e;
}
if (! $deal instanceof DealWithAssociations) {
throw new CrmException('Deal not found');
}
return [
'id' => $deal->getId(),
'properties' => $deal->getProperties(),
'associations' => $deal->getAssociations(),
];
}
/**
* Generic batch read method for HubSpot objects
*
* @param string $objectType The object type ('deals', 'companies', 'contacts')
* @param array<string> $crmIds Array of HubSpot object IDs (max 100)
* @param array<string> $fields Array of property names to fetch
*
* @return array<string, array> Array keyed by CRM ID with object data
*/
private function batchReadObjects(string $objectType, array $crmIds, array $fields): array
{
if (empty($crmIds)) {
return [];
}
$this->validateBatchSize($objectType, $crmIds);
$this->ensureValidToken();
try {
$batchConfig = $this->createBatchConfiguration($objectType);
$batchReadRequest = $this->prepareBatchRequest($batchConfig, $crmIds, $fields);
$response = $batchConfig['api']->read($batchReadRequest);
$this->validateApiResponse($response, $objectType);
$results = $this->processApiResults($response);
$this->logBatchResults($objectType, $crmIds, $results);
return $results;
} catch (\Throwable $e) {
$this->handleBatchError($e, $objectType, $crmIds);
}
}
private function validateBatchSize(string $objectType, array $crmIds): void
{
if (count($crmIds) > 100) {
throw new \InvalidArgumentException("Batch size cannot exceed 100 {$objectType}");
}
}
private function createBatchConfiguration(string $objectType): array
{
$configurations = [
'deals' => [
'batchReadRequest' => new \HubSpot\Client\Crm\Deals\Model\BatchReadInputSimplePublicObjectId(),
'inputClass' => \HubSpot\Client\Crm\Deals\Model\SimplePublicObjectId::class,
'api' => $this->getNewInstance()->crm()->deals()->batchApi(),
],
'companies' => [
'batchReadRequest' => new \HubSpot\Client\Crm\Companies\Model\BatchReadInputSimplePublicObjectId(),
'inputClass' => \HubSpot\Client\Crm\Companies\Model\SimplePublicObjectId::class,
'api' => $this->getNewInstance()->crm()->companies()->batchApi(),
],
'contacts' => [
'batchReadRequest' => new \HubSpot\Client\Crm\Contacts\Model\BatchReadInputSimplePublicObjectId(),
'inputClass' => \HubSpot\Client\Crm\Contacts\Model\SimplePublicObjectId::class,
'api' => $this->getNewInstance()->crm()->contacts()->batchApi(),
],
];
if (! isset($configurations[$objectType])) {
throw new \InvalidArgumentException("Unsupported object type: {$objectType}");
}
return $configurations[$objectType];
}
private function prepareBatchRequest(array $batchConfig, array $crmIds, array $fields): object
{
$batchReadRequest = $batchConfig['batchReadRequest'];
$inputClass = $batchConfig['inputClass'];
$inputs = array_map(function ($crmId) use ($inputClass) {
$input = new $inputClass();
$input->setId($crmId);
return $input;
}, $crmIds);
$batchReadRequest->setInputs($inputs);
$batchReadRequest->setProperties($fields);
return $batchReadRequest;
}
private function validateApiResponse($response, string $objectType): void
{
if (! $response) {
throw new CrmException("HubSpot API returned null response for {$objectType} batch read");
}
}
private function processApiResults($response): array
{
$results = [];
$responseResults = $response->getResults();
if ($responseResults) {
foreach ($responseResults as $object) {
if ($object && $object->getId()) {
$results[$object->getId()] = [
'id' => $object->getId(),
'properties' => $object->getProperties() ?: [],
];
}
}
}
return $results;
}
private function logBatchResults(string $objectType, array $crmIds, array $results): void
{
$this->log->info("[HubSpot] Batch fetched {$objectType}", [
'requested_count' => count($crmIds),
'returned_count' => count($results),
'crm_ids' => $crmIds,
]);
}
private function handleBatchError(\Throwable $e, string $objectType, array $crmIds): void
{
$errorMessage = $e->getMessage() ?: 'Unknown error';
$errorTrace = $e->getTraceAsString() ?: 'No trace available';
$this->log->error("[HubSpot] Failed to batch fetch {$objectType}", [
'crm_ids' => $crmIds,
'error' => $errorMessage,
'trace' => $errorTrace,
]);
throw new CrmException("Failed to batch fetch {$objectType}: " . $errorMessage);
}
/**
* Batch read multiple opportunities by their CRM IDs
*
* @param array<string> $crmIds Array of HubSpot deal IDs (max 100)
* @param array<string> $fields Array of property names to fetch
*
* @return array<string, array> Array keyed by CRM ID with opportunity data
*/
public function getOpportunitiesByIds(array $crmIds, array $fields): array
{
return $this->batchReadObjects('deals', $crmIds, $fields);
}
/**
* Batch read multiple companies by their CRM IDs
*
* @param array<string> $crmIds Array of HubSpot company IDs (max 100)
* @param array<string> $fields Array of property names to fetch
*
* @return array<string, array> Array keyed by CRM ID with company data
*/
public function getCompaniesByIds(array $crmIds, array $fields): array
{
return $this->batchReadObjects('companies', $crmIds, $fields);
}
/**
* Batch read multiple contacts by their CRM IDs
*
* @param array<string> $crmIds Array of HubSpot contact IDs (max 100)
* @param array<string> $fields Array of property names to fetch
*
* @return array<string, array> Array keyed by CRM ID with contact data
*/
public function getContactsByIds(array $crmIds, array $fields): array
{
return $this->batchReadObjects('contacts', $crmIds, $fields);
}
/**
* @throws CompanyApiException
* @throws CrmException
*/
public function getAccountById(string $crmId, array $fields): array
{
try {
$company = $this->getNewInstance()->crm()->companies()->basicApi()->getById(
$crmId,
implode(',', $fields),
);
} catch (CompanyApiException $e) {
$this->log->info('[Hubspot] Failed to fetch account', [
'crm_id' => $crmId,
'reason' => $e->getMessage(),
]);
throw $e;
}
if (! $company instanceof CompaniesWithAssociations) {
throw new CrmException('Account not found');
}
return [
'id' => $company->getId(),
'properties' => $company->getProperties(),
];
}
/**
* @throws ContactApiException
* @throws CrmException
*/
public function getContactById(string $crmId, array $fields): array
{
try {
$contact = $this->getNewInstance()->crm()->contacts()->basicApi()->getById(
$crmId,
implode(',', $fields)
);
} catch (ContactApiException $e) {
$this->log->info('[Hubspot] Failed to fetch contact', [
'crm_id' => $crmId,
'reason' => $e->getMessage(),
]);
throw $e;
}
if (! $contact instanceof ContactsWithAssociations) {
throw new CrmException('Contact not found');
}
return [
'id' => $contact->getId(),
'properties' => $contact->getProperties(),
];
}
/**
* This is email search request that Hubspot offers as GET (more generous quota)
*/
public function getContactByEmail(string $email, array $fields = []): array
{
try {
$contact = $this->getNewInstance()->crm()->contacts()->basicApi()->getById(
$email,
implode(',', $fields),
null,
false,
'email'
);
return [
'id' => $contact->getId(),
'properties' => $contact->getProperties(),
];
} catch (ContactApiException $e) {
$this->log->info('[Hubspot] Failed to fetch contact', [
'email' => $email,
'reason' => $e->getMessage(),
]);
return [];
}
}
/**
* @throws CrmException
*/
public function fetchProperty(string $objectType, string $propertyId): Property
{
$result = $this->getNewInstance()->crm()->properties()->coreApi()->getByName($objectType, $propertyId);
if (! $result instanceof Property) {
$this->log->error('[Hubspot] Failed to fetch property', [
'object_type' => $objectType,
'property_id' => $propertyId,
'reason' => $result->getMessage(),
]);
throw new CrmException('Failed to fetch property');
}
return $result;
}
/**
* @return array<CrmFieldOption>
*/
public function fetchPropertyOptions(string $objectType, string $propertyId): array
{
/** @var array<CrmFieldOption> */
return $this->fetchProperty($objectType, $propertyId)->getOptions();
}
/**
* @return array<array{id:string, label:string, deleted:bool}>
*/
public function fetchCallDispositions(): array
{
/** @var Response $response */
$response = $this->getInstance()->engagements()->getCallDispositions();
/**
* @var array<array{
* id:string,
* label:string,
* deleted: bool
* }>
*/
return $response->toArray();
}
/**
* @return array<CrmFieldOption>
*/
public function fetchOpportunityPipelineStages(): array
{
$stages = [];
$apiResponse = $this->getNewInstance()->crm()->pipelines()->pipelinesApi()->getAll('deals');
if ($apiResponse instanceof Error) {
$this->log->error('[Hubspot] Failed to fetch opportunity pipelines', [
'reason' => $apiResponse->getMessage(),
]);
return [];
}
foreach ($apiResponse->getResults() as $pipeline) {
$pipelineStages = array_map(
static function (PipelineStage $stage) {
return [
'id' => $stage->getId(),
'label' => $stage->getLabel(),
];
},
$pipeline->getStages()
);
$stages = array_merge($stages, $pipelineStages);
}
return $stages;
}
public function fetchOpportunityPipelines(): array
{
$pipelines = [];
try {
$apiResponse = $this->makeRequest('/crm/v3/pipelines/deals');
} catch (\Exception $e) {
$this->log->info('[Hubspot] Failed to fetch opportunity pipelines', [
'reason' => $e->getMessage(),
]);
return [];
}
$response = $apiResponse->toArray();
foreach ($response['results'] as $pipeline) {
$pipelines[] = [
'id' => $pipeline['id'],
'label' => $pipeline['label'],
];
}
return $pipelines;
}
/**
* @return array<CrmFieldOption>
*/
public function fetchMeetingOutcomeFieldOptions(Field $field): array
{
return $field->getCrmProviderId() === 'meetingOutcome'
? $this->fetchMeetingOutcomeTypes()
: $this->fetchCallActivityTypes();
}
public function fetchMeetingOutcomeTypes(): array
{
return $this->extractMeetingTypeOptions(
'[URL_WITH_CREDENTIALS] Response $response */
$response = $this->getInstance()
->getClient()
->request('GET', $endpoint);
/**
* @var array<array{
* value: string,
* label: string,
* displayOrder: int
* }> $optionData
*/
$optionData = $response->toArray()['options'] ?? [];
$options = [];
foreach ($optionData as $item) {
$options[] = [
'id' => $item['value'],
'value' => $item['value'],
'label' => $item['label'],
'display_order' => $item['displayOrder'],
];
}
return $options;
}
/**
* @return array<CrmFieldOption>
*/
public function fetchDispositionFieldOptions(): array
{
$options = [];
$dispositions = $this->fetchCallDispositions();
foreach ($dispositions as $disposition) {
if ($disposition['deleted'] !== false) {
continue;
}
$option['value'] = $disposition['id'];
$option['id'] = $disposition['id'];
$option['label'] = $disposition['label'];
$options[] = $option;
}
return $options;
}
/**
* @return array<CrmFieldOption>
*/
public function fetchOpportunityFieldOptions(Field $field): array
{
if ($field->isStageField()) {
return $this->fetchOpportunityPipelineStages();
}
if ($field->isPipelineField()) {
return $this->fetchOpportunityPipelines();
}
return $this->fetchPropertyOptions('deals', $field->getCrmProviderId());
}
/**
* @throws BadRequest
* @throws HubspotException
*/
public function makeRequest(string $endpoint, $method = 'GET', $payload = [], ?string $queryString = null)
{
$endpoint = self::BASE_URL . $endpoint;
if ($method === 'GET') {
return $this->getInstance()->getClient()?->request(
method: $method,
endpoint: $endpoint,
query_string: $queryString
);
} else {
return $this->getInstance()->getClient()->request($method, $endpoint, [
'json' => ($payload),
]);
}
}
/**
* @throws BadRequest
* @throws HubspotException
*/
public function createMeeting(array $payload): Response
{
$endpoint = '/crm/v3/objects/meetings';
return $this->makeRequest($endpoint, 'POST', $payload);
}
/**
* @throws BadRequest
* @throws HubspotException
*/
public function updateMeeting(string $meetingId, array $payload): Response
{
$endpoint = '/crm/v3/objects/meetings/' . $meetingId;
return $this->makeRequest($endpoint, 'PATCH', $payload);
}
/**
* @throws \Exception
*/
public function createNote(
string $body,
string $ownerId,
int $timestamp,
string $objectId,
NoteObject $noteObject
): ?string {
try {
$noteInput = new SimplePublicObjectInput([
'properties' => [
'hs_note_body' => $body,
'hubspot_owner_id' => $ownerId,
'hs_timestamp' => $timestamp,
],
]);
// Create note
$note = $this->getNewInstance()->crm()->objects()->basicApi()->create('note', $noteInput);
$this->getNewInstance()->crm()->objects()->associationsApi()->create(
'note',
$note->getId(),
$this->getNoteObject($noteObject),
$objectId,
$this->getNoteAssociationType($noteObject),
);
return $note->getId();
} catch (\Exception $e) {
$this->log->error('[Hubspot] Failed to create note', [
'objectId' => $objectId,
'noteObject' => $noteObject->getObjectType(),
'reason' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
return null;
}
public function updateEngagement(string $objectId, array $engagement, array $metadata): void
{
$this->getInstance()->engagements()->update($objectId, $engagement, $metadata);
}
public function getEngagementData(string $engagementId): array
{
$engagement = $this->getInstance()->engagements()->get($engagementId);
return $engagement->toArray();
}
public function createEngagement(array $engagement, array $associations, array $metadata): Response
{
return $this->getInstance()
->engagements()
->create($engagement, $associations, $metadata);
}
public function isUnauthorizedException(\Exception $e): bool
{
// Check for specific HubSpot API exception types first
if ($e instanceof BadRequest) {
// BadRequest can contain 401 status codes
return $e->getCode() === 401;
}
// Check for HTTP client exceptions with status codes
if ($e instanceof \GuzzleHttp\Exception\RequestException && $e->hasResponse()) {
$response = $e->getResponse();
if ($response !== null) {
return $response->getStatusCode() === 401;
}
}
// Check for Guzzle HTTP exceptions
if ($e instanceof \GuzzleHttp\Exception\ClientException) {
return $e->getCode() === 401;
}
// Fallback to string matching as last resort, but be more specific
$message = strtolower($e->getMessage());
return str_contains($message, '401 unauthorized') ||
str_contains($message, 'http 401') ||
str_contains($message, 'status code 401') ||
(preg_match('/\b401\b/', $message) && str_contains($message, 'unauthorized'));
}
/**
* Validates and refreshes the access token if needed before API requests.
* This ensures long-running processes don't fail due to token expiration.
*
* @throws SocialAccountTokenInvalidException
*/
public function ensureValidToken(): void
{
if ($this->oauthAccount === null) {
return;
}
$newToken = $this->tokenManager->ensureValidToken($this->oauthAccount);
if ($newToken !== null) {
$this->accessToken = $newToken;
}
}
public function getConfig()
{
return $this->config;
}
// returns only active (archived=false)
public function getOwners(): array
{
return $this->getNewInstance()->crm()->owners()->getAll();
}
/**
* @param bool $archived
*
* @return array<Owner>|[]
*/
public function getOwnersArchived(bool $archived = true): array
{
$endpoint = '/crm/v3/owners';
$queryParams = [
'archived' => $archived ? 'true' : 'false',
];
$queryString = http_build_query($queryParams);
$owners = [];
try {
$response = $this->makeRequest(endpoint: $endpoint, queryString: $queryString);
$responseData = $response?->toArray();
foreach ($responseData['results'] as $result) {
try {
$owners[] = Owner::create($result);
} catch (Throwable $e) {
$this->log->error('[HubSpot] Failed to process owner data', [
'result' => $result,
'error' => $e->getMessage(),
]);
continue;
}
}
} catch (Throwable $e) {
$this->log->error('HubSpot] Failed to fetch owners', [
'archived' => $archived,
'error' => $e->getMessage(),
]);
return [];
}
return $owners;
}
public function getMeeting(string $engagementId): ObjectWithAssociations
{
return $this->getNewInstance()->crm()->objects()->basicApi()
->getById('meeting', $engagementId, null, 'contact,company,deal');
}
public function deleteEngagement(string $engagementId): void
{
$this->getInstance()->engagements()->delete((int) $engagementId);
}
public function getAssociationsData(array $ids, string $fromObject, string $toObject): array
{
$associationData = [];
$idChunks = array_chunk($ids, self::ASSOCIATIONS_BATCH_SIZE_LIMIT);
foreach ($idChunks as $idChunk) {
try {
$batchInput = new \HubSpot\Client\Crm\Associations\Model\BatchInputPublicObjectId();
$batchInput->setInputs(array_map(function ($id) {
$publicObjectId = new \HubSpot\Client\Crm\Associations\Model\PublicObjectId();
$publicObjectId->setId($id);
return $publicObjectId;
}, $idChunk));
$associatedObjectsData = $this
->getNewInstance()
->crm()
->associations()
->batchApi()
->read($fromObject, $toObject, $batchInput);
if ($associatedObjectsData instanceof \HubSpot\Client\Crm\Associations\Model\BatchResponsePublicAssociationMulti) {
foreach ($associatedObjectsData->getResults() as $association) {
$from = $association->getFrom()->getId();
$toAssociations = $association->getTo();
if (! empty($toAssociations)) {
$associationData[$from] = array_map(function ($item) {
return $item->getId();
}, $toAssociations);
}
}
}
// } catch (RateLimitException $e) {
// throw $e;
} catch (\Exception $e) {
$this->log->error('[Hubspot] Failed to fetch associations', [
'from_object' => $fromObject,
'to_object' => $toObject,
'reason' => $e->getMessage(),
]);
}
}
return $associationData;
}
/**
* @throws \Exception
*/
private function getNoteAssociationType(NoteObject $noteObject): string
{
return match($noteObject) {
NoteObject::Opportunity => 'note_to_deal',
NoteObject::Lead, NoteObject::Contact => 'note_to_contact', // or 'note_to_lead' if your portal supports it
NoteObject::Account => 'note_to_company',
NoteObject::Call, NoteObject::Event => throw new \Exception('Not supported'),
};
}
/**
* @throws \Exception
*/
private function getNoteObject(NoteObject $noteObject): string
{
return match($noteObject) {
NoteObject::Opportunity => 'deal',
NoteObject::Lead, NoteObject::Contact => 'contact',
NoteObject::Account => 'company',
NoteObject::Call, NoteObject::Event => throw new \Exception('Not supported'),
};
}
public function addAssociations(string $objectType, string $associationType, array $payload): Response
{
$endpoint = "/crm/v4/associations/$objectType/$associationType/batch/create";
return $this->makeRequest($endpoint, 'POST', $payload);
}
public function removeAssociations(string $objectType, string $associationType, array $payload): Response
{
$endpoint = "/crm/v4/associations/$objectType/$associationType/batch/archive";
return $this->makeRequest($endpoint, 'POST', $payload);
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
19
Previous Highlighted Error...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"bounds":{"left":0.025930852,"top":0.019952115,"width":0.03856383,"height":0.025538707},"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JY-20725-handle-HS-search-rate-limit, menu","depth":5,"bounds":{"left":0.064494684,"top":0.019952115,"width":0.09541223,"height":0.025538707},"on_screen":true,"help_text":"Git Branch: JY-20725-handle-HS-search-rate-limit","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"bounds":{"left":0.82413566,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"HandleHubspotRateLimitTest","depth":6,"bounds":{"left":0.8394282,"top":0.019952115,"width":0.076130316,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'HandleHubspotRateLimitTest'","depth":6,"bounds":{"left":0.9155585,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'HandleHubspotRateLimitTest'","depth":6,"bounds":{"left":0.9268617,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"bounds":{"left":0.9381649,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"bounds":{"left":0.96609044,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"bounds":{"left":0.9773936,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"bounds":{"left":0.9886968,"top":0.019952115,"width":0.011303186,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"2","depth":4,"bounds":{"left":0.41156915,"top":0.19952115,"width":0.007978723,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"67","depth":4,"bounds":{"left":0.42154256,"top":0.19952115,"width":0.009973404,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"3","depth":4,"bounds":{"left":0.43351063,"top":0.19952115,"width":0.007978723,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.4431516,"top":0.19792499,"width":0.00731383,"height":0.018355945},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.4504654,"top":0.19792499,"width":0.006981383,"height":0.018355945},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Services\\Crm\\Hubspot;\n\nuse HubSpot\\Client\\Crm\\Deals\\ApiException as DealApiException;\nuse HubSpot\\Client\\Crm\\Contacts\\ApiException as ContactApiException;\nuse HubSpot\\Client\\Crm\\Companies\\ApiException as CompanyApiException;\nuse HubSpot\\Client\\Crm\\Contacts\\Model\\SimplePublicObjectWithAssociations as ContactsWithAssociations;\nuse HubSpot\\Client\\Crm\\Companies\\Model\\SimplePublicObjectWithAssociations as CompaniesWithAssociations;\nuse HubSpot\\Client\\Crm\\Deals\\Model\\SimplePublicObjectWithAssociations as DealWithAssociations;\nuse HubSpot\\Client\\Crm\\Objects\\Model\\SimplePublicObjectInput;\nuse HubSpot\\Client\\Crm\\Objects\\Model\\SimplePublicObjectWithAssociations as ObjectWithAssociations;\nuse HubSpot\\Client\\Crm\\Pipelines\\Model\\Error;\nuse HubSpot\\Client\\Crm\\Pipelines\\Model\\PipelineStage;\nuse HubSpot\\Client\\Crm\\Properties\\Model\\Property;\nuse HubSpot\\Discovery\\Discovery;\nuse Jiminny\\Exceptions\\CrmException;\nuse Jiminny\\Exceptions\\RateLimitException;\nuse Jiminny\\Exceptions\\SocialAccountTokenInvalidException;\nuse Jiminny\\Jobs\\Crm\\NoteObject;\nuse Jiminny\\Models\\Crm\\Field;\nuse Jiminny\\Services\\Crm\\BaseClient;\nuse Jiminny\\Services\\Crm\\Hubspot\\DTO\\Response\\Owner;\nuse Jiminny\\Services\\SocialAccountService;\nuse SevenShores\\Hubspot\\Exceptions\\BadRequest;\nuse SevenShores\\Hubspot\\Exceptions\\HubspotException;\nuse SevenShores\\Hubspot\\Factory;\nuse SevenShores\\Hubspot\\Http\\Response;\nuse Jiminny\\Services\\Crm\\Hubspot\\Pagination\\HubspotPaginationService;\nuse Illuminate\\Support\\Facades\\Redis;\nuse Throwable;\n\n/**\n * @phpstan-type CrmFieldOption array{id:string, label:string, value?:string}\n */\nclass Client extends BaseClient implements HubspotClientInterface\n{\n public const string MIN_API_VERSION = '2';\n\n public const string BASE_URL = 'https://api.hubapi.com';\n\n public const int ASSOCIATIONS_BATCH_SIZE_LIMIT = 1000;\n\n private HubspotPaginationService $paginationService;\n private HubspotTokenManager $tokenManager;\n\n public function __construct(\n SocialAccountService $socialAccountService,\n HubspotPaginationService $paginationService,\n HubspotTokenManager $tokenManager\n ) {\n parent::__construct($socialAccountService);\n $this->paginationService = $paginationService;\n $this->tokenManager = $tokenManager;\n\n $this->setBaseUrl(self::BASE_URL);\n $this->setVersion(self::MIN_API_VERSION);\n }\n\n /**\n * Reacts to a rate limits (429) from HubSpot by translating it\n * into a RateLimitException carrying retry_after.\n *\n * Wrap any outbound HubSpot call (SDK or raw HTTP) like:\n *\n * $this->executeRequest(fn () => $this->getNewInstance()->crm()->...);\n *\n * @template T\n * @param callable(): T $apiCall\n * @return T\n *\n * @throws RateLimitException\n */\n private function executeRequest(callable $apiCall)\n {\n $cacheKey = $this->getRateLimitCacheKey();\n\n $cachedRetryAfter = Redis::get($cacheKey);\n if (is_string($cachedRetryAfter) && is_numeric($cachedRetryAfter)) {\n throw new RateLimitException(\n 'Hubspot rate limit (cached circuit-breaker)',\n (int) $cachedRetryAfter,\n );\n }\n\n try {\n return $apiCall();\n } catch (Throwable $e) {\n if ($this->isHubspotRateLimit($e)) {\n $retryAfter = $this->parseRetryAfter($e);\n\n Redis::setex($cacheKey, $retryAfter, (string) $retryAfter);\n\n $this->log->warning('[Hubspot] Received 429 from API', [\n 'team_id' => $this->config->team_id,\n 'config_id' => $this->config->getId(),\n 'retry_after' => $retryAfter,\n 'message' => $this->parseMessage($e),\n 'reason' => $e->getMessage(),\n ]);\n\n throw new RateLimitException('Hubspot returned 429', $retryAfter, $e);\n }\n\n throw $e;\n }\n }\n\n private function getRateLimitCacheKey(): string\n {\n return sprintf('hubspot:ratelimit:portal:%d', $this->config->getId());\n }\n\n public function isHubspotRateLimit(Throwable $e): bool\n {\n if ($e instanceof BadRequest\n || $e instanceof DealApiException\n || $e instanceof ContactApiException\n || $e instanceof CompanyApiException\n || $e instanceof \\GuzzleHttp\\Exception\\RequestException\n ) {\n return (int) $e->getCode() === 429;\n }\n\n return false;\n }\n\n public function parseRetryAfter(Throwable $e): int\n {\n if (method_exists($e, 'getResponseHeaders')) {\n $headers = $e->getResponseHeaders() ?: [];\n $value = $headers['Retry-After'] ?? $headers['retry-after'] ?? null;\n if (is_array($value)) {\n $value = $value[0] ?? null;\n }\n if (is_numeric($value)) {\n return (int) $value;\n }\n }\n\n $message = strtolower($this->parseMessage($e));\n\n if (str_contains($message, 'daily')) {\n return 600;\n }\n if (str_contains($message, 'ten secondly')) {\n return 10;\n }\n if (str_contains($message, 'secondly')) {\n return 1;\n }\n\n $this->log->warning('[Hubspot] No retry-after header or known message, using default', [\n 'exception_class' => get_class($e),\n 'message' => $message,\n ]);\n\n return 10;\n }\n\n public function parseMessage(Throwable $e): string\n {\n if (! method_exists($e, 'getResponseBody')) {\n return '';\n }\n\n $body = $e->getResponseBody();\n if (is_string($body)) {\n $body = json_decode($body, true) ?? [];\n }\n\n if (! is_array($body)) {\n return '';\n }\n\n return (string) ($body['message'] ?? '');\n }\n\n public function getMinimumApiVersion(): string\n {\n return self::MIN_API_VERSION;\n }\n\n public function getInstance(): Factory\n {\n return new Factory([\n 'key' => $this->accessToken,\n 'oauth2' => true,\n 'base_url' => $this->baseUrl,\n ]);\n }\n\n public function getNewInstance(): Discovery\n {\n return \\HubSpot\\Factory::createWithAccessToken($this->accessToken);\n }\n\n /**\n * Secondly and daily limits for Hubspot API\n *\n * Product Tier: Free & Starter | Professional & Enterprise | API add-on (any tier)\n * Burst: 100/10 seconds | 150/10 seconds | 200/10 seconds\n * Daily: 250,000 | 500,000 | 1,000,000\n *\n * Official documentation states: The search endpoints are rate limited to five requests per second.\n * Since with 5 RPS were still hitting secondly rate limits we lowered it to 4\n */\n public function getPaginatedData(array $payload, string $type, int $offset = 0): array\n {\n $total = 0;\n $lastId = null;\n $rows = [];\n foreach ($this->getPaginatedDataGenerator($payload, $type, $offset, $total, $lastId) as $row) {\n $rows[] = $row;\n }\n\n return ['results' => $rows, 'total' => $total, 'last_record' => $lastId];\n }\n\n /**\n * @throws HubspotException\n * @throws SocialAccountTokenInvalidException\n * @throws BadRequest\n */\n public function getPaginatedDataGenerator(\n array $payload,\n string $type,\n int $offset = 0,\n int &$total = 0,\n ?string &$lastRecordId = null\n ): \\Generator {\n return $this->paginationService->getPaginatedDataGenerator(\n $this,\n $payload,\n $type,\n $offset,\n $total,\n $lastRecordId\n );\n }\n\n /**\n * Execute a search request against HubSpot CRM objects with rate limiting.\n *\n * @param string $objectType The object type ('deals', 'companies', 'contacts', 'calls')\n * @param array<string, mixed> $payload The search payload with filters, sorts, properties, etc.\n * @return array The search response with 'results', 'total', 'paging' keys\n * @throws RateLimitException When rate limit is hit\n * @throws HubspotException On API errors\n */\n public function search(string $objectType, array $payload): array\n {\n $endpoint = self::BASE_URL . \"/crm/v3/objects/{$objectType}/search\";\n\n return $this->executeRequest(function () use ($endpoint, $payload) {\n $response = $this->getInstance()->getClient()->request('POST', $endpoint, ['json' => $payload]);\n\n return $response->toArray();\n });\n }\n\n /**\n * @throws DealApiException\n * @throws CrmException\n */\n public function getOpportunityById(string $crmId, array $fields): array\n {\n try {\n// $deal = $this->executeRequest(fn () => $this->getNewInstance()->crm()->deals()->basicApi()->getById(\n $deal = $this->getNewInstance()->crm()->deals()->basicApi()->getById(\n $crmId,\n implode(',', $fields),\n 'companies,contacts'\n );\n } catch (DealApiException $e) {\n $this->log->info('[Hubspot] Failed to fetch opportunity', [\n 'crm_id' => $crmId,\n 'reason' => $e->getMessage(),\n ]);\n\n throw $e;\n }\n\n if (! $deal instanceof DealWithAssociations) {\n throw new CrmException('Deal not found');\n }\n\n return [\n 'id' => $deal->getId(),\n 'properties' => $deal->getProperties(),\n 'associations' => $deal->getAssociations(),\n ];\n }\n\n /**\n * Generic batch read method for HubSpot objects\n *\n * @param string $objectType The object type ('deals', 'companies', 'contacts')\n * @param array<string> $crmIds Array of HubSpot object IDs (max 100)\n * @param array<string> $fields Array of property names to fetch\n *\n * @return array<string, array> Array keyed by CRM ID with object data\n */\n private function batchReadObjects(string $objectType, array $crmIds, array $fields): array\n {\n if (empty($crmIds)) {\n return [];\n }\n\n $this->validateBatchSize($objectType, $crmIds);\n $this->ensureValidToken();\n\n try {\n $batchConfig = $this->createBatchConfiguration($objectType);\n $batchReadRequest = $this->prepareBatchRequest($batchConfig, $crmIds, $fields);\n $response = $batchConfig['api']->read($batchReadRequest);\n\n $this->validateApiResponse($response, $objectType);\n\n $results = $this->processApiResults($response);\n $this->logBatchResults($objectType, $crmIds, $results);\n\n return $results;\n } catch (\\Throwable $e) {\n $this->handleBatchError($e, $objectType, $crmIds);\n }\n }\n\n private function validateBatchSize(string $objectType, array $crmIds): void\n {\n if (count($crmIds) > 100) {\n throw new \\InvalidArgumentException(\"Batch size cannot exceed 100 {$objectType}\");\n }\n }\n\n private function createBatchConfiguration(string $objectType): array\n {\n $configurations = [\n 'deals' => [\n 'batchReadRequest' => new \\HubSpot\\Client\\Crm\\Deals\\Model\\BatchReadInputSimplePublicObjectId(),\n 'inputClass' => \\HubSpot\\Client\\Crm\\Deals\\Model\\SimplePublicObjectId::class,\n 'api' => $this->getNewInstance()->crm()->deals()->batchApi(),\n ],\n 'companies' => [\n 'batchReadRequest' => new \\HubSpot\\Client\\Crm\\Companies\\Model\\BatchReadInputSimplePublicObjectId(),\n 'inputClass' => \\HubSpot\\Client\\Crm\\Companies\\Model\\SimplePublicObjectId::class,\n 'api' => $this->getNewInstance()->crm()->companies()->batchApi(),\n ],\n 'contacts' => [\n 'batchReadRequest' => new \\HubSpot\\Client\\Crm\\Contacts\\Model\\BatchReadInputSimplePublicObjectId(),\n 'inputClass' => \\HubSpot\\Client\\Crm\\Contacts\\Model\\SimplePublicObjectId::class,\n 'api' => $this->getNewInstance()->crm()->contacts()->batchApi(),\n ],\n ];\n\n if (! isset($configurations[$objectType])) {\n throw new \\InvalidArgumentException(\"Unsupported object type: {$objectType}\");\n }\n\n return $configurations[$objectType];\n }\n\n private function prepareBatchRequest(array $batchConfig, array $crmIds, array $fields): object\n {\n $batchReadRequest = $batchConfig['batchReadRequest'];\n $inputClass = $batchConfig['inputClass'];\n\n $inputs = array_map(function ($crmId) use ($inputClass) {\n $input = new $inputClass();\n $input->setId($crmId);\n\n return $input;\n }, $crmIds);\n\n $batchReadRequest->setInputs($inputs);\n $batchReadRequest->setProperties($fields);\n\n return $batchReadRequest;\n }\n\n private function validateApiResponse($response, string $objectType): void\n {\n if (! $response) {\n throw new CrmException(\"HubSpot API returned null response for {$objectType} batch read\");\n }\n }\n\n private function processApiResults($response): array\n {\n $results = [];\n $responseResults = $response->getResults();\n\n if ($responseResults) {\n foreach ($responseResults as $object) {\n if ($object && $object->getId()) {\n $results[$object->getId()] = [\n 'id' => $object->getId(),\n 'properties' => $object->getProperties() ?: [],\n ];\n }\n }\n }\n\n return $results;\n }\n\n private function logBatchResults(string $objectType, array $crmIds, array $results): void\n {\n $this->log->info(\"[HubSpot] Batch fetched {$objectType}\", [\n 'requested_count' => count($crmIds),\n 'returned_count' => count($results),\n 'crm_ids' => $crmIds,\n ]);\n }\n\n private function handleBatchError(\\Throwable $e, string $objectType, array $crmIds): void\n {\n $errorMessage = $e->getMessage() ?: 'Unknown error';\n $errorTrace = $e->getTraceAsString() ?: 'No trace available';\n\n $this->log->error(\"[HubSpot] Failed to batch fetch {$objectType}\", [\n 'crm_ids' => $crmIds,\n 'error' => $errorMessage,\n 'trace' => $errorTrace,\n ]);\n\n throw new CrmException(\"Failed to batch fetch {$objectType}: \" . $errorMessage);\n }\n\n /**\n * Batch read multiple opportunities by their CRM IDs\n *\n * @param array<string> $crmIds Array of HubSpot deal IDs (max 100)\n * @param array<string> $fields Array of property names to fetch\n *\n * @return array<string, array> Array keyed by CRM ID with opportunity data\n */\n public function getOpportunitiesByIds(array $crmIds, array $fields): array\n {\n return $this->batchReadObjects('deals', $crmIds, $fields);\n }\n\n /**\n * Batch read multiple companies by their CRM IDs\n *\n * @param array<string> $crmIds Array of HubSpot company IDs (max 100)\n * @param array<string> $fields Array of property names to fetch\n *\n * @return array<string, array> Array keyed by CRM ID with company data\n */\n public function getCompaniesByIds(array $crmIds, array $fields): array\n {\n return $this->batchReadObjects('companies', $crmIds, $fields);\n }\n\n /**\n * Batch read multiple contacts by their CRM IDs\n *\n * @param array<string> $crmIds Array of HubSpot contact IDs (max 100)\n * @param array<string> $fields Array of property names to fetch\n *\n * @return array<string, array> Array keyed by CRM ID with contact data\n */\n public function getContactsByIds(array $crmIds, array $fields): array\n {\n return $this->batchReadObjects('contacts', $crmIds, $fields);\n }\n\n /**\n * @throws CompanyApiException\n * @throws CrmException\n */\n public function getAccountById(string $crmId, array $fields): array\n {\n try {\n $company = $this->getNewInstance()->crm()->companies()->basicApi()->getById(\n $crmId,\n implode(',', $fields),\n );\n } catch (CompanyApiException $e) {\n $this->log->info('[Hubspot] Failed to fetch account', [\n 'crm_id' => $crmId,\n 'reason' => $e->getMessage(),\n ]);\n\n throw $e;\n }\n\n if (! $company instanceof CompaniesWithAssociations) {\n throw new CrmException('Account not found');\n }\n\n return [\n 'id' => $company->getId(),\n 'properties' => $company->getProperties(),\n ];\n }\n\n /**\n * @throws ContactApiException\n * @throws CrmException\n */\n public function getContactById(string $crmId, array $fields): array\n {\n try {\n $contact = $this->getNewInstance()->crm()->contacts()->basicApi()->getById(\n $crmId,\n implode(',', $fields)\n );\n } catch (ContactApiException $e) {\n $this->log->info('[Hubspot] Failed to fetch contact', [\n 'crm_id' => $crmId,\n 'reason' => $e->getMessage(),\n ]);\n\n throw $e;\n }\n\n if (! $contact instanceof ContactsWithAssociations) {\n throw new CrmException('Contact not found');\n }\n\n return [\n 'id' => $contact->getId(),\n 'properties' => $contact->getProperties(),\n ];\n }\n\n /**\n * This is email search request that Hubspot offers as GET (more generous quota)\n */\n public function getContactByEmail(string $email, array $fields = []): array\n {\n try {\n $contact = $this->getNewInstance()->crm()->contacts()->basicApi()->getById(\n $email,\n implode(',', $fields),\n null,\n false,\n 'email'\n );\n\n return [\n 'id' => $contact->getId(),\n 'properties' => $contact->getProperties(),\n ];\n } catch (ContactApiException $e) {\n $this->log->info('[Hubspot] Failed to fetch contact', [\n 'email' => $email,\n 'reason' => $e->getMessage(),\n ]);\n\n return [];\n }\n }\n\n /**\n * @throws CrmException\n */\n public function fetchProperty(string $objectType, string $propertyId): Property\n {\n $result = $this->getNewInstance()->crm()->properties()->coreApi()->getByName($objectType, $propertyId);\n\n if (! $result instanceof Property) {\n $this->log->error('[Hubspot] Failed to fetch property', [\n 'object_type' => $objectType,\n 'property_id' => $propertyId,\n 'reason' => $result->getMessage(),\n ]);\n\n throw new CrmException('Failed to fetch property');\n }\n\n return $result;\n }\n\n /**\n * @return array<CrmFieldOption>\n */\n public function fetchPropertyOptions(string $objectType, string $propertyId): array\n {\n /** @var array<CrmFieldOption> */\n return $this->fetchProperty($objectType, $propertyId)->getOptions();\n }\n\n /**\n * @return array<array{id:string, label:string, deleted:bool}>\n */\n public function fetchCallDispositions(): array\n {\n /** @var Response $response */\n $response = $this->getInstance()->engagements()->getCallDispositions();\n\n /**\n * @var array<array{\n * id:string,\n * label:string,\n * deleted: bool\n * }>\n */\n return $response->toArray();\n }\n\n /**\n * @return array<CrmFieldOption>\n */\n public function fetchOpportunityPipelineStages(): array\n {\n $stages = [];\n $apiResponse = $this->getNewInstance()->crm()->pipelines()->pipelinesApi()->getAll('deals');\n\n if ($apiResponse instanceof Error) {\n $this->log->error('[Hubspot] Failed to fetch opportunity pipelines', [\n 'reason' => $apiResponse->getMessage(),\n ]);\n\n return [];\n }\n\n foreach ($apiResponse->getResults() as $pipeline) {\n $pipelineStages = array_map(\n static function (PipelineStage $stage) {\n return [\n 'id' => $stage->getId(),\n 'label' => $stage->getLabel(),\n ];\n },\n $pipeline->getStages()\n );\n\n $stages = array_merge($stages, $pipelineStages);\n }\n\n return $stages;\n }\n\n public function fetchOpportunityPipelines(): array\n {\n $pipelines = [];\n\n try {\n $apiResponse = $this->makeRequest('/crm/v3/pipelines/deals');\n } catch (\\Exception $e) {\n $this->log->info('[Hubspot] Failed to fetch opportunity pipelines', [\n 'reason' => $e->getMessage(),\n ]);\n\n return [];\n }\n\n $response = $apiResponse->toArray();\n\n foreach ($response['results'] as $pipeline) {\n $pipelines[] = [\n 'id' => $pipeline['id'],\n 'label' => $pipeline['label'],\n ];\n }\n\n return $pipelines;\n }\n\n /**\n * @return array<CrmFieldOption>\n */\n public function fetchMeetingOutcomeFieldOptions(Field $field): array\n {\n return $field->getCrmProviderId() === 'meetingOutcome'\n ? $this->fetchMeetingOutcomeTypes()\n : $this->fetchCallActivityTypes();\n }\n\n public function fetchMeetingOutcomeTypes(): array\n {\n return $this->extractMeetingTypeOptions(\n 'https://api.hubapi.com/crm/v3/properties/meeting/hs_meeting_outcome'\n );\n }\n\n public function fetchCallActivityTypes(): array\n {\n return $this->extractMeetingTypeOptions(\n 'https://api.hubapi.com/crm/v3/properties/call/hs_activity_type'\n );\n }\n\n private function extractMeetingTypeOptions(string $endpoint): array\n {\n /** @var Response $response */\n $response = $this->getInstance()\n ->getClient()\n ->request('GET', $endpoint);\n\n /**\n * @var array<array{\n * value: string,\n * label: string,\n * displayOrder: int\n * }> $optionData\n */\n $optionData = $response->toArray()['options'] ?? [];\n\n $options = [];\n foreach ($optionData as $item) {\n $options[] = [\n 'id' => $item['value'],\n 'value' => $item['value'],\n 'label' => $item['label'],\n 'display_order' => $item['displayOrder'],\n ];\n }\n\n return $options;\n }\n\n /**\n * @return array<CrmFieldOption>\n */\n public function fetchDispositionFieldOptions(): array\n {\n $options = [];\n\n $dispositions = $this->fetchCallDispositions();\n\n foreach ($dispositions as $disposition) {\n if ($disposition['deleted'] !== false) {\n continue;\n }\n\n $option['value'] = $disposition['id'];\n $option['id'] = $disposition['id'];\n $option['label'] = $disposition['label'];\n\n $options[] = $option;\n }\n\n return $options;\n }\n\n /**\n * @return array<CrmFieldOption>\n */\n public function fetchOpportunityFieldOptions(Field $field): array\n {\n if ($field->isStageField()) {\n return $this->fetchOpportunityPipelineStages();\n }\n\n if ($field->isPipelineField()) {\n return $this->fetchOpportunityPipelines();\n }\n\n return $this->fetchPropertyOptions('deals', $field->getCrmProviderId());\n }\n\n /**\n * @throws BadRequest\n * @throws HubspotException\n */\n public function makeRequest(string $endpoint, $method = 'GET', $payload = [], ?string $queryString = null)\n {\n $endpoint = self::BASE_URL . $endpoint;\n\n if ($method === 'GET') {\n return $this->getInstance()->getClient()?->request(\n method: $method,\n endpoint: $endpoint,\n query_string: $queryString\n );\n } else {\n return $this->getInstance()->getClient()->request($method, $endpoint, [\n 'json' => ($payload),\n ]);\n }\n }\n\n /**\n * @throws BadRequest\n * @throws HubspotException\n */\n public function createMeeting(array $payload): Response\n {\n $endpoint = '/crm/v3/objects/meetings';\n\n return $this->makeRequest($endpoint, 'POST', $payload);\n }\n\n /**\n * @throws BadRequest\n * @throws HubspotException\n */\n public function updateMeeting(string $meetingId, array $payload): Response\n {\n $endpoint = '/crm/v3/objects/meetings/' . $meetingId;\n\n return $this->makeRequest($endpoint, 'PATCH', $payload);\n }\n\n /**\n * @throws \\Exception\n */\n public function createNote(\n string $body,\n string $ownerId,\n int $timestamp,\n string $objectId,\n NoteObject $noteObject\n ): ?string {\n try {\n $noteInput = new SimplePublicObjectInput([\n 'properties' => [\n 'hs_note_body' => $body,\n 'hubspot_owner_id' => $ownerId,\n 'hs_timestamp' => $timestamp,\n ],\n ]);\n\n // Create note\n $note = $this->getNewInstance()->crm()->objects()->basicApi()->create('note', $noteInput);\n\n $this->getNewInstance()->crm()->objects()->associationsApi()->create(\n 'note',\n $note->getId(),\n $this->getNoteObject($noteObject),\n $objectId,\n $this->getNoteAssociationType($noteObject),\n );\n\n return $note->getId();\n } catch (\\Exception $e) {\n $this->log->error('[Hubspot] Failed to create note', [\n 'objectId' => $objectId,\n 'noteObject' => $noteObject->getObjectType(),\n 'reason' => $e->getMessage(),\n ]);\n\n \\Sentry::captureException($e);\n }\n\n return null;\n }\n\n public function updateEngagement(string $objectId, array $engagement, array $metadata): void\n {\n $this->getInstance()->engagements()->update($objectId, $engagement, $metadata);\n }\n\n public function getEngagementData(string $engagementId): array\n {\n $engagement = $this->getInstance()->engagements()->get($engagementId);\n\n return $engagement->toArray();\n }\n\n public function createEngagement(array $engagement, array $associations, array $metadata): Response\n {\n return $this->getInstance()\n ->engagements()\n ->create($engagement, $associations, $metadata);\n }\n\n public function isUnauthorizedException(\\Exception $e): bool\n {\n // Check for specific HubSpot API exception types first\n if ($e instanceof BadRequest) {\n // BadRequest can contain 401 status codes\n return $e->getCode() === 401;\n }\n\n // Check for HTTP client exceptions with status codes\n if ($e instanceof \\GuzzleHttp\\Exception\\RequestException && $e->hasResponse()) {\n $response = $e->getResponse();\n if ($response !== null) {\n return $response->getStatusCode() === 401;\n }\n }\n\n // Check for Guzzle HTTP exceptions\n if ($e instanceof \\GuzzleHttp\\Exception\\ClientException) {\n return $e->getCode() === 401;\n }\n\n // Fallback to string matching as last resort, but be more specific\n $message = strtolower($e->getMessage());\n\n return str_contains($message, '401 unauthorized') ||\n str_contains($message, 'http 401') ||\n str_contains($message, 'status code 401') ||\n (preg_match('/\\b401\\b/', $message) && str_contains($message, 'unauthorized'));\n }\n\n /**\n * Validates and refreshes the access token if needed before API requests.\n * This ensures long-running processes don't fail due to token expiration.\n *\n * @throws SocialAccountTokenInvalidException\n */\n public function ensureValidToken(): void\n {\n if ($this->oauthAccount === null) {\n return;\n }\n\n $newToken = $this->tokenManager->ensureValidToken($this->oauthAccount);\n if ($newToken !== null) {\n $this->accessToken = $newToken;\n }\n }\n\n public function getConfig()\n {\n return $this->config;\n }\n\n // returns only active (archived=false)\n public function getOwners(): array\n {\n return $this->getNewInstance()->crm()->owners()->getAll();\n }\n\n /**\n * @param bool $archived\n *\n * @return array<Owner>|[]\n */\n public function getOwnersArchived(bool $archived = true): array\n {\n $endpoint = '/crm/v3/owners';\n $queryParams = [\n 'archived' => $archived ? 'true' : 'false',\n ];\n $queryString = http_build_query($queryParams);\n\n $owners = [];\n\n try {\n $response = $this->makeRequest(endpoint: $endpoint, queryString: $queryString);\n $responseData = $response?->toArray();\n\n foreach ($responseData['results'] as $result) {\n try {\n $owners[] = Owner::create($result);\n } catch (Throwable $e) {\n $this->log->error('[HubSpot] Failed to process owner data', [\n 'result' => $result,\n 'error' => $e->getMessage(),\n ]);\n\n continue;\n }\n }\n } catch (Throwable $e) {\n $this->log->error('HubSpot] Failed to fetch owners', [\n 'archived' => $archived,\n 'error' => $e->getMessage(),\n ]);\n\n return [];\n }\n\n return $owners;\n }\n\n public function getMeeting(string $engagementId): ObjectWithAssociations\n {\n return $this->getNewInstance()->crm()->objects()->basicApi()\n ->getById('meeting', $engagementId, null, 'contact,company,deal');\n }\n\n public function deleteEngagement(string $engagementId): void\n {\n $this->getInstance()->engagements()->delete((int) $engagementId);\n }\n\n public function getAssociationsData(array $ids, string $fromObject, string $toObject): array\n {\n $associationData = [];\n $idChunks = array_chunk($ids, self::ASSOCIATIONS_BATCH_SIZE_LIMIT);\n\n foreach ($idChunks as $idChunk) {\n try {\n $batchInput = new \\HubSpot\\Client\\Crm\\Associations\\Model\\BatchInputPublicObjectId();\n $batchInput->setInputs(array_map(function ($id) {\n $publicObjectId = new \\HubSpot\\Client\\Crm\\Associations\\Model\\PublicObjectId();\n $publicObjectId->setId($id);\n\n return $publicObjectId;\n }, $idChunk));\n\n $associatedObjectsData = $this\n ->getNewInstance()\n ->crm()\n ->associations()\n ->batchApi()\n ->read($fromObject, $toObject, $batchInput);\n\n if ($associatedObjectsData instanceof \\HubSpot\\Client\\Crm\\Associations\\Model\\BatchResponsePublicAssociationMulti) {\n foreach ($associatedObjectsData->getResults() as $association) {\n $from = $association->getFrom()->getId();\n $toAssociations = $association->getTo();\n\n if (! empty($toAssociations)) {\n $associationData[$from] = array_map(function ($item) {\n return $item->getId();\n }, $toAssociations);\n }\n }\n }\n// } catch (RateLimitException $e) {\n// throw $e;\n } catch (\\Exception $e) {\n $this->log->error('[Hubspot] Failed to fetch associations', [\n 'from_object' => $fromObject,\n 'to_object' => $toObject,\n 'reason' => $e->getMessage(),\n ]);\n }\n }\n\n return $associationData;\n }\n\n /**\n * @throws \\Exception\n */\n private function getNoteAssociationType(NoteObject $noteObject): string\n {\n return match($noteObject) {\n NoteObject::Opportunity => 'note_to_deal',\n NoteObject::Lead, NoteObject::Contact => 'note_to_contact', // or 'note_to_lead' if your portal supports it\n NoteObject::Account => 'note_to_company',\n NoteObject::Call, NoteObject::Event => throw new \\Exception('Not supported'),\n };\n }\n\n /**\n * @throws \\Exception\n */\n private function getNoteObject(NoteObject $noteObject): string\n {\n return match($noteObject) {\n NoteObject::Opportunity => 'deal',\n NoteObject::Lead, NoteObject::Contact => 'contact',\n NoteObject::Account => 'company',\n NoteObject::Call, NoteObject::Event => throw new \\Exception('Not supported'),\n };\n }\n\n public function addAssociations(string $objectType, string $associationType, array $payload): Response\n {\n $endpoint = \"/crm/v4/associations/$objectType/$associationType/batch/create\";\n\n return $this->makeRequest($endpoint, 'POST', $payload);\n }\n\n public function removeAssociations(string $objectType, string $associationType, array $payload): Response\n {\n $endpoint = \"/crm/v4/associations/$objectType/$associationType/batch/archive\";\n\n return $this->makeRequest($endpoint, 'POST', $payload);\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Services\\Crm\\Hubspot;\n\nuse HubSpot\\Client\\Crm\\Deals\\ApiException as DealApiException;\nuse HubSpot\\Client\\Crm\\Contacts\\ApiException as ContactApiException;\nuse HubSpot\\Client\\Crm\\Companies\\ApiException as CompanyApiException;\nuse HubSpot\\Client\\Crm\\Contacts\\Model\\SimplePublicObjectWithAssociations as ContactsWithAssociations;\nuse HubSpot\\Client\\Crm\\Companies\\Model\\SimplePublicObjectWithAssociations as CompaniesWithAssociations;\nuse HubSpot\\Client\\Crm\\Deals\\Model\\SimplePublicObjectWithAssociations as DealWithAssociations;\nuse HubSpot\\Client\\Crm\\Objects\\Model\\SimplePublicObjectInput;\nuse HubSpot\\Client\\Crm\\Objects\\Model\\SimplePublicObjectWithAssociations as ObjectWithAssociations;\nuse HubSpot\\Client\\Crm\\Pipelines\\Model\\Error;\nuse HubSpot\\Client\\Crm\\Pipelines\\Model\\PipelineStage;\nuse HubSpot\\Client\\Crm\\Properties\\Model\\Property;\nuse HubSpot\\Discovery\\Discovery;\nuse Jiminny\\Exceptions\\CrmException;\nuse Jiminny\\Exceptions\\RateLimitException;\nuse Jiminny\\Exceptions\\SocialAccountTokenInvalidException;\nuse Jiminny\\Jobs\\Crm\\NoteObject;\nuse Jiminny\\Models\\Crm\\Field;\nuse Jiminny\\Services\\Crm\\BaseClient;\nuse Jiminny\\Services\\Crm\\Hubspot\\DTO\\Response\\Owner;\nuse Jiminny\\Services\\SocialAccountService;\nuse SevenShores\\Hubspot\\Exceptions\\BadRequest;\nuse SevenShores\\Hubspot\\Exceptions\\HubspotException;\nuse SevenShores\\Hubspot\\Factory;\nuse SevenShores\\Hubspot\\Http\\Response;\nuse Jiminny\\Services\\Crm\\Hubspot\\Pagination\\HubspotPaginationService;\nuse Illuminate\\Support\\Facades\\Redis;\nuse Throwable;\n\n/**\n * @phpstan-type CrmFieldOption array{id:string, label:string, value?:string}\n */\nclass Client extends BaseClient implements HubspotClientInterface\n{\n public const string MIN_API_VERSION = '2';\n\n public const string BASE_URL = 'https://api.hubapi.com';\n\n public const int ASSOCIATIONS_BATCH_SIZE_LIMIT = 1000;\n\n private HubspotPaginationService $paginationService;\n private HubspotTokenManager $tokenManager;\n\n public function __construct(\n SocialAccountService $socialAccountService,\n HubspotPaginationService $paginationService,\n HubspotTokenManager $tokenManager\n ) {\n parent::__construct($socialAccountService);\n $this->paginationService = $paginationService;\n $this->tokenManager = $tokenManager;\n\n $this->setBaseUrl(self::BASE_URL);\n $this->setVersion(self::MIN_API_VERSION);\n }\n\n /**\n * Reacts to a rate limits (429) from HubSpot by translating it\n * into a RateLimitException carrying retry_after.\n *\n * Wrap any outbound HubSpot call (SDK or raw HTTP) like:\n *\n * $this->executeRequest(fn () => $this->getNewInstance()->crm()->...);\n *\n * @template T\n * @param callable(): T $apiCall\n * @return T\n *\n * @throws RateLimitException\n */\n private function executeRequest(callable $apiCall)\n {\n $cacheKey = $this->getRateLimitCacheKey();\n\n $cachedRetryAfter = Redis::get($cacheKey);\n if (is_string($cachedRetryAfter) && is_numeric($cachedRetryAfter)) {\n throw new RateLimitException(\n 'Hubspot rate limit (cached circuit-breaker)',\n (int) $cachedRetryAfter,\n );\n }\n\n try {\n return $apiCall();\n } catch (Throwable $e) {\n if ($this->isHubspotRateLimit($e)) {\n $retryAfter = $this->parseRetryAfter($e);\n\n Redis::setex($cacheKey, $retryAfter, (string) $retryAfter);\n\n $this->log->warning('[Hubspot] Received 429 from API', [\n 'team_id' => $this->config->team_id,\n 'config_id' => $this->config->getId(),\n 'retry_after' => $retryAfter,\n 'message' => $this->parseMessage($e),\n 'reason' => $e->getMessage(),\n ]);\n\n throw new RateLimitException('Hubspot returned 429', $retryAfter, $e);\n }\n\n throw $e;\n }\n }\n\n private function getRateLimitCacheKey(): string\n {\n return sprintf('hubspot:ratelimit:portal:%d', $this->config->getId());\n }\n\n public function isHubspotRateLimit(Throwable $e): bool\n {\n if ($e instanceof BadRequest\n || $e instanceof DealApiException\n || $e instanceof ContactApiException\n || $e instanceof CompanyApiException\n || $e instanceof \\GuzzleHttp\\Exception\\RequestException\n ) {\n return (int) $e->getCode() === 429;\n }\n\n return false;\n }\n\n public function parseRetryAfter(Throwable $e): int\n {\n if (method_exists($e, 'getResponseHeaders')) {\n $headers = $e->getResponseHeaders() ?: [];\n $value = $headers['Retry-After'] ?? $headers['retry-after'] ?? null;\n if (is_array($value)) {\n $value = $value[0] ?? null;\n }\n if (is_numeric($value)) {\n return (int) $value;\n }\n }\n\n $message = strtolower($this->parseMessage($e));\n\n if (str_contains($message, 'daily')) {\n return 600;\n }\n if (str_contains($message, 'ten secondly')) {\n return 10;\n }\n if (str_contains($message, 'secondly')) {\n return 1;\n }\n\n $this->log->warning('[Hubspot] No retry-after header or known message, using default', [\n 'exception_class' => get_class($e),\n 'message' => $message,\n ]);\n\n return 10;\n }\n\n public function parseMessage(Throwable $e): string\n {\n if (! method_exists($e, 'getResponseBody')) {\n return '';\n }\n\n $body = $e->getResponseBody();\n if (is_string($body)) {\n $body = json_decode($body, true) ?? [];\n }\n\n if (! is_array($body)) {\n return '';\n }\n\n return (string) ($body['message'] ?? '');\n }\n\n public function getMinimumApiVersion(): string\n {\n return self::MIN_API_VERSION;\n }\n\n public function getInstance(): Factory\n {\n return new Factory([\n 'key' => $this->accessToken,\n 'oauth2' => true,\n 'base_url' => $this->baseUrl,\n ]);\n }\n\n public function getNewInstance(): Discovery\n {\n return \\HubSpot\\Factory::createWithAccessToken($this->accessToken);\n }\n\n /**\n * Secondly and daily limits for Hubspot API\n *\n * Product Tier: Free & Starter | Professional & Enterprise | API add-on (any tier)\n * Burst: 100/10 seconds | 150/10 seconds | 200/10 seconds\n * Daily: 250,000 | 500,000 | 1,000,000\n *\n * Official documentation states: The search endpoints are rate limited to five requests per second.\n * Since with 5 RPS were still hitting secondly rate limits we lowered it to 4\n */\n public function getPaginatedData(array $payload, string $type, int $offset = 0): array\n {\n $total = 0;\n $lastId = null;\n $rows = [];\n foreach ($this->getPaginatedDataGenerator($payload, $type, $offset, $total, $lastId) as $row) {\n $rows[] = $row;\n }\n\n return ['results' => $rows, 'total' => $total, 'last_record' => $lastId];\n }\n\n /**\n * @throws HubspotException\n * @throws SocialAccountTokenInvalidException\n * @throws BadRequest\n */\n public function getPaginatedDataGenerator(\n array $payload,\n string $type,\n int $offset = 0,\n int &$total = 0,\n ?string &$lastRecordId = null\n ): \\Generator {\n return $this->paginationService->getPaginatedDataGenerator(\n $this,\n $payload,\n $type,\n $offset,\n $total,\n $lastRecordId\n );\n }\n\n /**\n * Execute a search request against HubSpot CRM objects with rate limiting.\n *\n * @param string $objectType The object type ('deals', 'companies', 'contacts', 'calls')\n * @param array<string, mixed> $payload The search payload with filters, sorts, properties, etc.\n * @return array The search response with 'results', 'total', 'paging' keys\n * @throws RateLimitException When rate limit is hit\n * @throws HubspotException On API errors\n */\n public function search(string $objectType, array $payload): array\n {\n $endpoint = self::BASE_URL . \"/crm/v3/objects/{$objectType}/search\";\n\n return $this->executeRequest(function () use ($endpoint, $payload) {\n $response = $this->getInstance()->getClient()->request('POST', $endpoint, ['json' => $payload]);\n\n return $response->toArray();\n });\n }\n\n /**\n * @throws DealApiException\n * @throws CrmException\n */\n public function getOpportunityById(string $crmId, array $fields): array\n {\n try {\n// $deal = $this->executeRequest(fn () => $this->getNewInstance()->crm()->deals()->basicApi()->getById(\n $deal = $this->getNewInstance()->crm()->deals()->basicApi()->getById(\n $crmId,\n implode(',', $fields),\n 'companies,contacts'\n );\n } catch (DealApiException $e) {\n $this->log->info('[Hubspot] Failed to fetch opportunity', [\n 'crm_id' => $crmId,\n 'reason' => $e->getMessage(),\n ]);\n\n throw $e;\n }\n\n if (! $deal instanceof DealWithAssociations) {\n throw new CrmException('Deal not found');\n }\n\n return [\n 'id' => $deal->getId(),\n 'properties' => $deal->getProperties(),\n 'associations' => $deal->getAssociations(),\n ];\n }\n\n /**\n * Generic batch read method for HubSpot objects\n *\n * @param string $objectType The object type ('deals', 'companies', 'contacts')\n * @param array<string> $crmIds Array of HubSpot object IDs (max 100)\n * @param array<string> $fields Array of property names to fetch\n *\n * @return array<string, array> Array keyed by CRM ID with object data\n */\n private function batchReadObjects(string $objectType, array $crmIds, array $fields): array\n {\n if (empty($crmIds)) {\n return [];\n }\n\n $this->validateBatchSize($objectType, $crmIds);\n $this->ensureValidToken();\n\n try {\n $batchConfig = $this->createBatchConfiguration($objectType);\n $batchReadRequest = $this->prepareBatchRequest($batchConfig, $crmIds, $fields);\n $response = $batchConfig['api']->read($batchReadRequest);\n\n $this->validateApiResponse($response, $objectType);\n\n $results = $this->processApiResults($response);\n $this->logBatchResults($objectType, $crmIds, $results);\n\n return $results;\n } catch (\\Throwable $e) {\n $this->handleBatchError($e, $objectType, $crmIds);\n }\n }\n\n private function validateBatchSize(string $objectType, array $crmIds): void\n {\n if (count($crmIds) > 100) {\n throw new \\InvalidArgumentException(\"Batch size cannot exceed 100 {$objectType}\");\n }\n }\n\n private function createBatchConfiguration(string $objectType): array\n {\n $configurations = [\n 'deals' => [\n 'batchReadRequest' => new \\HubSpot\\Client\\Crm\\Deals\\Model\\BatchReadInputSimplePublicObjectId(),\n 'inputClass' => \\HubSpot\\Client\\Crm\\Deals\\Model\\SimplePublicObjectId::class,\n 'api' => $this->getNewInstance()->crm()->deals()->batchApi(),\n ],\n 'companies' => [\n 'batchReadRequest' => new \\HubSpot\\Client\\Crm\\Companies\\Model\\BatchReadInputSimplePublicObjectId(),\n 'inputClass' => \\HubSpot\\Client\\Crm\\Companies\\Model\\SimplePublicObjectId::class,\n 'api' => $this->getNewInstance()->crm()->companies()->batchApi(),\n ],\n 'contacts' => [\n 'batchReadRequest' => new \\HubSpot\\Client\\Crm\\Contacts\\Model\\BatchReadInputSimplePublicObjectId(),\n 'inputClass' => \\HubSpot\\Client\\Crm\\Contacts\\Model\\SimplePublicObjectId::class,\n 'api' => $this->getNewInstance()->crm()->contacts()->batchApi(),\n ],\n ];\n\n if (! isset($configurations[$objectType])) {\n throw new \\InvalidArgumentException(\"Unsupported object type: {$objectType}\");\n }\n\n return $configurations[$objectType];\n }\n\n private function prepareBatchRequest(array $batchConfig, array $crmIds, array $fields): object\n {\n $batchReadRequest = $batchConfig['batchReadRequest'];\n $inputClass = $batchConfig['inputClass'];\n\n $inputs = array_map(function ($crmId) use ($inputClass) {\n $input = new $inputClass();\n $input->setId($crmId);\n\n return $input;\n }, $crmIds);\n\n $batchReadRequest->setInputs($inputs);\n $batchReadRequest->setProperties($fields);\n\n return $batchReadRequest;\n }\n\n private function validateApiResponse($response, string $objectType): void\n {\n if (! $response) {\n throw new CrmException(\"HubSpot API returned null response for {$objectType} batch read\");\n }\n }\n\n private function processApiResults($response): array\n {\n $results = [];\n $responseResults = $response->getResults();\n\n if ($responseResults) {\n foreach ($responseResults as $object) {\n if ($object && $object->getId()) {\n $results[$object->getId()] = [\n 'id' => $object->getId(),\n 'properties' => $object->getProperties() ?: [],\n ];\n }\n }\n }\n\n return $results;\n }\n\n private function logBatchResults(string $objectType, array $crmIds, array $results): void\n {\n $this->log->info(\"[HubSpot] Batch fetched {$objectType}\", [\n 'requested_count' => count($crmIds),\n 'returned_count' => count($results),\n 'crm_ids' => $crmIds,\n ]);\n }\n\n private function handleBatchError(\\Throwable $e, string $objectType, array $crmIds): void\n {\n $errorMessage = $e->getMessage() ?: 'Unknown error';\n $errorTrace = $e->getTraceAsString() ?: 'No trace available';\n\n $this->log->error(\"[HubSpot] Failed to batch fetch {$objectType}\", [\n 'crm_ids' => $crmIds,\n 'error' => $errorMessage,\n 'trace' => $errorTrace,\n ]);\n\n throw new CrmException(\"Failed to batch fetch {$objectType}: \" . $errorMessage);\n }\n\n /**\n * Batch read multiple opportunities by their CRM IDs\n *\n * @param array<string> $crmIds Array of HubSpot deal IDs (max 100)\n * @param array<string> $fields Array of property names to fetch\n *\n * @return array<string, array> Array keyed by CRM ID with opportunity data\n */\n public function getOpportunitiesByIds(array $crmIds, array $fields): array\n {\n return $this->batchReadObjects('deals', $crmIds, $fields);\n }\n\n /**\n * Batch read multiple companies by their CRM IDs\n *\n * @param array<string> $crmIds Array of HubSpot company IDs (max 100)\n * @param array<string> $fields Array of property names to fetch\n *\n * @return array<string, array> Array keyed by CRM ID with company data\n */\n public function getCompaniesByIds(array $crmIds, array $fields): array\n {\n return $this->batchReadObjects('companies', $crmIds, $fields);\n }\n\n /**\n * Batch read multiple contacts by their CRM IDs\n *\n * @param array<string> $crmIds Array of HubSpot contact IDs (max 100)\n * @param array<string> $fields Array of property names to fetch\n *\n * @return array<string, array> Array keyed by CRM ID with contact data\n */\n public function getContactsByIds(array $crmIds, array $fields): array\n {\n return $this->batchReadObjects('contacts', $crmIds, $fields);\n }\n\n /**\n * @throws CompanyApiException\n * @throws CrmException\n */\n public function getAccountById(string $crmId, array $fields): array\n {\n try {\n $company = $this->getNewInstance()->crm()->companies()->basicApi()->getById(\n $crmId,\n implode(',', $fields),\n );\n } catch (CompanyApiException $e) {\n $this->log->info('[Hubspot] Failed to fetch account', [\n 'crm_id' => $crmId,\n 'reason' => $e->getMessage(),\n ]);\n\n throw $e;\n }\n\n if (! $company instanceof CompaniesWithAssociations) {\n throw new CrmException('Account not found');\n }\n\n return [\n 'id' => $company->getId(),\n 'properties' => $company->getProperties(),\n ];\n }\n\n /**\n * @throws ContactApiException\n * @throws CrmException\n */\n public function getContactById(string $crmId, array $fields): array\n {\n try {\n $contact = $this->getNewInstance()->crm()->contacts()->basicApi()->getById(\n $crmId,\n implode(',', $fields)\n );\n } catch (ContactApiException $e) {\n $this->log->info('[Hubspot] Failed to fetch contact', [\n 'crm_id' => $crmId,\n 'reason' => $e->getMessage(),\n ]);\n\n throw $e;\n }\n\n if (! $contact instanceof ContactsWithAssociations) {\n throw new CrmException('Contact not found');\n }\n\n return [\n 'id' => $contact->getId(),\n 'properties' => $contact->getProperties(),\n ];\n }\n\n /**\n * This is email search request that Hubspot offers as GET (more generous quota)\n */\n public function getContactByEmail(string $email, array $fields = []): array\n {\n try {\n $contact = $this->getNewInstance()->crm()->contacts()->basicApi()->getById(\n $email,\n implode(',', $fields),\n null,\n false,\n 'email'\n );\n\n return [\n 'id' => $contact->getId(),\n 'properties' => $contact->getProperties(),\n ];\n } catch (ContactApiException $e) {\n $this->log->info('[Hubspot] Failed to fetch contact', [\n 'email' => $email,\n 'reason' => $e->getMessage(),\n ]);\n\n return [];\n }\n }\n\n /**\n * @throws CrmException\n */\n public function fetchProperty(string $objectType, string $propertyId): Property\n {\n $result = $this->getNewInstance()->crm()->properties()->coreApi()->getByName($objectType, $propertyId);\n\n if (! $result instanceof Property) {\n $this->log->error('[Hubspot] Failed to fetch property', [\n 'object_type' => $objectType,\n 'property_id' => $propertyId,\n 'reason' => $result->getMessage(),\n ]);\n\n throw new CrmException('Failed to fetch property');\n }\n\n return $result;\n }\n\n /**\n * @return array<CrmFieldOption>\n */\n public function fetchPropertyOptions(string $objectType, string $propertyId): array\n {\n /** @var array<CrmFieldOption> */\n return $this->fetchProperty($objectType, $propertyId)->getOptions();\n }\n\n /**\n * @return array<array{id:string, label:string, deleted:bool}>\n */\n public function fetchCallDispositions(): array\n {\n /** @var Response $response */\n $response = $this->getInstance()->engagements()->getCallDispositions();\n\n /**\n * @var array<array{\n * id:string,\n * label:string,\n * deleted: bool\n * }>\n */\n return $response->toArray();\n }\n\n /**\n * @return array<CrmFieldOption>\n */\n public function fetchOpportunityPipelineStages(): array\n {\n $stages = [];\n $apiResponse = $this->getNewInstance()->crm()->pipelines()->pipelinesApi()->getAll('deals');\n\n if ($apiResponse instanceof Error) {\n $this->log->error('[Hubspot] Failed to fetch opportunity pipelines', [\n 'reason' => $apiResponse->getMessage(),\n ]);\n\n return [];\n }\n\n foreach ($apiResponse->getResults() as $pipeline) {\n $pipelineStages = array_map(\n static function (PipelineStage $stage) {\n return [\n 'id' => $stage->getId(),\n 'label' => $stage->getLabel(),\n ];\n },\n $pipeline->getStages()\n );\n\n $stages = array_merge($stages, $pipelineStages);\n }\n\n return $stages;\n }\n\n public function fetchOpportunityPipelines(): array\n {\n $pipelines = [];\n\n try {\n $apiResponse = $this->makeRequest('/crm/v3/pipelines/deals');\n } catch (\\Exception $e) {\n $this->log->info('[Hubspot] Failed to fetch opportunity pipelines', [\n 'reason' => $e->getMessage(),\n ]);\n\n return [];\n }\n\n $response = $apiResponse->toArray();\n\n foreach ($response['results'] as $pipeline) {\n $pipelines[] = [\n 'id' => $pipeline['id'],\n 'label' => $pipeline['label'],\n ];\n }\n\n return $pipelines;\n }\n\n /**\n * @return array<CrmFieldOption>\n */\n public function fetchMeetingOutcomeFieldOptions(Field $field): array\n {\n return $field->getCrmProviderId() === 'meetingOutcome'\n ? $this->fetchMeetingOutcomeTypes()\n : $this->fetchCallActivityTypes();\n }\n\n public function fetchMeetingOutcomeTypes(): array\n {\n return $this->extractMeetingTypeOptions(\n 'https://api.hubapi.com/crm/v3/properties/meeting/hs_meeting_outcome'\n );\n }\n\n public function fetchCallActivityTypes(): array\n {\n return $this->extractMeetingTypeOptions(\n 'https://api.hubapi.com/crm/v3/properties/call/hs_activity_type'\n );\n }\n\n private function extractMeetingTypeOptions(string $endpoint): array\n {\n /** @var Response $response */\n $response = $this->getInstance()\n ->getClient()\n ->request('GET', $endpoint);\n\n /**\n * @var array<array{\n * value: string,\n * label: string,\n * displayOrder: int\n * }> $optionData\n */\n $optionData = $response->toArray()['options'] ?? [];\n\n $options = [];\n foreach ($optionData as $item) {\n $options[] = [\n 'id' => $item['value'],\n 'value' => $item['value'],\n 'label' => $item['label'],\n 'display_order' => $item['displayOrder'],\n ];\n }\n\n return $options;\n }\n\n /**\n * @return array<CrmFieldOption>\n */\n public function fetchDispositionFieldOptions(): array\n {\n $options = [];\n\n $dispositions = $this->fetchCallDispositions();\n\n foreach ($dispositions as $disposition) {\n if ($disposition['deleted'] !== false) {\n continue;\n }\n\n $option['value'] = $disposition['id'];\n $option['id'] = $disposition['id'];\n $option['label'] = $disposition['label'];\n\n $options[] = $option;\n }\n\n return $options;\n }\n\n /**\n * @return array<CrmFieldOption>\n */\n public function fetchOpportunityFieldOptions(Field $field): array\n {\n if ($field->isStageField()) {\n return $this->fetchOpportunityPipelineStages();\n }\n\n if ($field->isPipelineField()) {\n return $this->fetchOpportunityPipelines();\n }\n\n return $this->fetchPropertyOptions('deals', $field->getCrmProviderId());\n }\n\n /**\n * @throws BadRequest\n * @throws HubspotException\n */\n public function makeRequest(string $endpoint, $method = 'GET', $payload = [], ?string $queryString = null)\n {\n $endpoint = self::BASE_URL . $endpoint;\n\n if ($method === 'GET') {\n return $this->getInstance()->getClient()?->request(\n method: $method,\n endpoint: $endpoint,\n query_string: $queryString\n );\n } else {\n return $this->getInstance()->getClient()->request($method, $endpoint, [\n 'json' => ($payload),\n ]);\n }\n }\n\n /**\n * @throws BadRequest\n * @throws HubspotException\n */\n public function createMeeting(array $payload): Response\n {\n $endpoint = '/crm/v3/objects/meetings';\n\n return $this->makeRequest($endpoint, 'POST', $payload);\n }\n\n /**\n * @throws BadRequest\n * @throws HubspotException\n */\n public function updateMeeting(string $meetingId, array $payload): Response\n {\n $endpoint = '/crm/v3/objects/meetings/' . $meetingId;\n\n return $this->makeRequest($endpoint, 'PATCH', $payload);\n }\n\n /**\n * @throws \\Exception\n */\n public function createNote(\n string $body,\n string $ownerId,\n int $timestamp,\n string $objectId,\n NoteObject $noteObject\n ): ?string {\n try {\n $noteInput = new SimplePublicObjectInput([\n 'properties' => [\n 'hs_note_body' => $body,\n 'hubspot_owner_id' => $ownerId,\n 'hs_timestamp' => $timestamp,\n ],\n ]);\n\n // Create note\n $note = $this->getNewInstance()->crm()->objects()->basicApi()->create('note', $noteInput);\n\n $this->getNewInstance()->crm()->objects()->associationsApi()->create(\n 'note',\n $note->getId(),\n $this->getNoteObject($noteObject),\n $objectId,\n $this->getNoteAssociationType($noteObject),\n );\n\n return $note->getId();\n } catch (\\Exception $e) {\n $this->log->error('[Hubspot] Failed to create note', [\n 'objectId' => $objectId,\n 'noteObject' => $noteObject->getObjectType(),\n 'reason' => $e->getMessage(),\n ]);\n\n \\Sentry::captureException($e);\n }\n\n return null;\n }\n\n public function updateEngagement(string $objectId, array $engagement, array $metadata): void\n {\n $this->getInstance()->engagements()->update($objectId, $engagement, $metadata);\n }\n\n public function getEngagementData(string $engagementId): array\n {\n $engagement = $this->getInstance()->engagements()->get($engagementId);\n\n return $engagement->toArray();\n }\n\n public function createEngagement(array $engagement, array $associations, array $metadata): Response\n {\n return $this->getInstance()\n ->engagements()\n ->create($engagement, $associations, $metadata);\n }\n\n public function isUnauthorizedException(\\Exception $e): bool\n {\n // Check for specific HubSpot API exception types first\n if ($e instanceof BadRequest) {\n // BadRequest can contain 401 status codes\n return $e->getCode() === 401;\n }\n\n // Check for HTTP client exceptions with status codes\n if ($e instanceof \\GuzzleHttp\\Exception\\RequestException && $e->hasResponse()) {\n $response = $e->getResponse();\n if ($response !== null) {\n return $response->getStatusCode() === 401;\n }\n }\n\n // Check for Guzzle HTTP exceptions\n if ($e instanceof \\GuzzleHttp\\Exception\\ClientException) {\n return $e->getCode() === 401;\n }\n\n // Fallback to string matching as last resort, but be more specific\n $message = strtolower($e->getMessage());\n\n return str_contains($message, '401 unauthorized') ||\n str_contains($message, 'http 401') ||\n str_contains($message, 'status code 401') ||\n (preg_match('/\\b401\\b/', $message) && str_contains($message, 'unauthorized'));\n }\n\n /**\n * Validates and refreshes the access token if needed before API requests.\n * This ensures long-running processes don't fail due to token expiration.\n *\n * @throws SocialAccountTokenInvalidException\n */\n public function ensureValidToken(): void\n {\n if ($this->oauthAccount === null) {\n return;\n }\n\n $newToken = $this->tokenManager->ensureValidToken($this->oauthAccount);\n if ($newToken !== null) {\n $this->accessToken = $newToken;\n }\n }\n\n public function getConfig()\n {\n return $this->config;\n }\n\n // returns only active (archived=false)\n public function getOwners(): array\n {\n return $this->getNewInstance()->crm()->owners()->getAll();\n }\n\n /**\n * @param bool $archived\n *\n * @return array<Owner>|[]\n */\n public function getOwnersArchived(bool $archived = true): array\n {\n $endpoint = '/crm/v3/owners';\n $queryParams = [\n 'archived' => $archived ? 'true' : 'false',\n ];\n $queryString = http_build_query($queryParams);\n\n $owners = [];\n\n try {\n $response = $this->makeRequest(endpoint: $endpoint, queryString: $queryString);\n $responseData = $response?->toArray();\n\n foreach ($responseData['results'] as $result) {\n try {\n $owners[] = Owner::create($result);\n } catch (Throwable $e) {\n $this->log->error('[HubSpot] Failed to process owner data', [\n 'result' => $result,\n 'error' => $e->getMessage(),\n ]);\n\n continue;\n }\n }\n } catch (Throwable $e) {\n $this->log->error('HubSpot] Failed to fetch owners', [\n 'archived' => $archived,\n 'error' => $e->getMessage(),\n ]);\n\n return [];\n }\n\n return $owners;\n }\n\n public function getMeeting(string $engagementId): ObjectWithAssociations\n {\n return $this->getNewInstance()->crm()->objects()->basicApi()\n ->getById('meeting', $engagementId, null, 'contact,company,deal');\n }\n\n public function deleteEngagement(string $engagementId): void\n {\n $this->getInstance()->engagements()->delete((int) $engagementId);\n }\n\n public function getAssociationsData(array $ids, string $fromObject, string $toObject): array\n {\n $associationData = [];\n $idChunks = array_chunk($ids, self::ASSOCIATIONS_BATCH_SIZE_LIMIT);\n\n foreach ($idChunks as $idChunk) {\n try {\n $batchInput = new \\HubSpot\\Client\\Crm\\Associations\\Model\\BatchInputPublicObjectId();\n $batchInput->setInputs(array_map(function ($id) {\n $publicObjectId = new \\HubSpot\\Client\\Crm\\Associations\\Model\\PublicObjectId();\n $publicObjectId->setId($id);\n\n return $publicObjectId;\n }, $idChunk));\n\n $associatedObjectsData = $this\n ->getNewInstance()\n ->crm()\n ->associations()\n ->batchApi()\n ->read($fromObject, $toObject, $batchInput);\n\n if ($associatedObjectsData instanceof \\HubSpot\\Client\\Crm\\Associations\\Model\\BatchResponsePublicAssociationMulti) {\n foreach ($associatedObjectsData->getResults() as $association) {\n $from = $association->getFrom()->getId();\n $toAssociations = $association->getTo();\n\n if (! empty($toAssociations)) {\n $associationData[$from] = array_map(function ($item) {\n return $item->getId();\n }, $toAssociations);\n }\n }\n }\n// } catch (RateLimitException $e) {\n// throw $e;\n } catch (\\Exception $e) {\n $this->log->error('[Hubspot] Failed to fetch associations', [\n 'from_object' => $fromObject,\n 'to_object' => $toObject,\n 'reason' => $e->getMessage(),\n ]);\n }\n }\n\n return $associationData;\n }\n\n /**\n * @throws \\Exception\n */\n private function getNoteAssociationType(NoteObject $noteObject): string\n {\n return match($noteObject) {\n NoteObject::Opportunity => 'note_to_deal',\n NoteObject::Lead, NoteObject::Contact => 'note_to_contact', // or 'note_to_lead' if your portal supports it\n NoteObject::Account => 'note_to_company',\n NoteObject::Call, NoteObject::Event => throw new \\Exception('Not supported'),\n };\n }\n\n /**\n * @throws \\Exception\n */\n private function getNoteObject(NoteObject $noteObject): string\n {\n return match($noteObject) {\n NoteObject::Opportunity => 'deal',\n NoteObject::Lead, NoteObject::Contact => 'contact',\n NoteObject::Account => 'company',\n NoteObject::Call, NoteObject::Event => throw new \\Exception('Not supported'),\n };\n }\n\n public function addAssociations(string $objectType, string $associationType, array $payload): Response\n {\n $endpoint = \"/crm/v4/associations/$objectType/$associationType/batch/create\";\n\n return $this->makeRequest($endpoint, 'POST', $payload);\n }\n\n public function removeAssociations(string $objectType, string $associationType, array $payload): Response\n {\n $endpoint = \"/crm/v4/associations/$objectType/$associationType/batch/archive\";\n\n return $this->makeRequest($endpoint, 'POST', $payload);\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":true,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"19","depth":4,"bounds":{"left":0.7124335,"top":0.10055866,"width":0.009640957,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.7237367,"top":0.09896249,"width":0.00731383,"height":0.018355945},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
8811914002027558382
|
6378759348859832420
|
click
|
accessibility
|
NULL
|
Project: faVsco.js, menu
JY-20725-handle-HS-search Project: faVsco.js, menu
JY-20725-handle-HS-search-rate-limit, menu
Start Listening for PHP Debug Connections
HandleHubspotRateLimitTest
Run 'HandleHubspotRateLimitTest'
Debug 'HandleHubspotRateLimitTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
2
67
3
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Services\Crm\Hubspot;
use HubSpot\Client\Crm\Deals\ApiException as DealApiException;
use HubSpot\Client\Crm\Contacts\ApiException as ContactApiException;
use HubSpot\Client\Crm\Companies\ApiException as CompanyApiException;
use HubSpot\Client\Crm\Contacts\Model\SimplePublicObjectWithAssociations as ContactsWithAssociations;
use HubSpot\Client\Crm\Companies\Model\SimplePublicObjectWithAssociations as CompaniesWithAssociations;
use HubSpot\Client\Crm\Deals\Model\SimplePublicObjectWithAssociations as DealWithAssociations;
use HubSpot\Client\Crm\Objects\Model\SimplePublicObjectInput;
use HubSpot\Client\Crm\Objects\Model\SimplePublicObjectWithAssociations as ObjectWithAssociations;
use HubSpot\Client\Crm\Pipelines\Model\Error;
use HubSpot\Client\Crm\Pipelines\Model\PipelineStage;
use HubSpot\Client\Crm\Properties\Model\Property;
use HubSpot\Discovery\Discovery;
use Jiminny\Exceptions\CrmException;
use Jiminny\Exceptions\RateLimitException;
use Jiminny\Exceptions\SocialAccountTokenInvalidException;
use Jiminny\Jobs\Crm\NoteObject;
use Jiminny\Models\Crm\Field;
use Jiminny\Services\Crm\BaseClient;
use Jiminny\Services\Crm\Hubspot\DTO\Response\Owner;
use Jiminny\Services\SocialAccountService;
use SevenShores\Hubspot\Exceptions\BadRequest;
use SevenShores\Hubspot\Exceptions\HubspotException;
use SevenShores\Hubspot\Factory;
use SevenShores\Hubspot\Http\Response;
use Jiminny\Services\Crm\Hubspot\Pagination\HubspotPaginationService;
use Illuminate\Support\Facades\Redis;
use Throwable;
/**
* @phpstan-type CrmFieldOption array{id:string, label:string, value?:string}
*/
class Client extends BaseClient implements HubspotClientInterface
{
public const string MIN_API_VERSION = '2';
public const string BASE_URL = '[URL_WITH_CREDENTIALS] T
* @param callable(): T $apiCall
* @return T
*
* @throws RateLimitException
*/
private function executeRequest(callable $apiCall)
{
$cacheKey = $this->getRateLimitCacheKey();
$cachedRetryAfter = Redis::get($cacheKey);
if (is_string($cachedRetryAfter) && is_numeric($cachedRetryAfter)) {
throw new RateLimitException(
'Hubspot rate limit (cached circuit-breaker)',
(int) $cachedRetryAfter,
);
}
try {
return $apiCall();
} catch (Throwable $e) {
if ($this->isHubspotRateLimit($e)) {
$retryAfter = $this->parseRetryAfter($e);
Redis::setex($cacheKey, $retryAfter, (string) $retryAfter);
$this->log->warning('[Hubspot] Received 429 from API', [
'team_id' => $this->config->team_id,
'config_id' => $this->config->getId(),
'retry_after' => $retryAfter,
'message' => $this->parseMessage($e),
'reason' => $e->getMessage(),
]);
throw new RateLimitException('Hubspot returned 429', $retryAfter, $e);
}
throw $e;
}
}
private function getRateLimitCacheKey(): string
{
return sprintf('hubspot:ratelimit:portal:%d', $this->config->getId());
}
public function isHubspotRateLimit(Throwable $e): bool
{
if ($e instanceof BadRequest
|| $e instanceof DealApiException
|| $e instanceof ContactApiException
|| $e instanceof CompanyApiException
|| $e instanceof \GuzzleHttp\Exception\RequestException
) {
return (int) $e->getCode() === 429;
}
return false;
}
public function parseRetryAfter(Throwable $e): int
{
if (method_exists($e, 'getResponseHeaders')) {
$headers = $e->getResponseHeaders() ?: [];
$value = $headers['Retry-After'] ?? $headers['retry-after'] ?? null;
if (is_array($value)) {
$value = $value[0] ?? null;
}
if (is_numeric($value)) {
return (int) $value;
}
}
$message = strtolower($this->parseMessage($e));
if (str_contains($message, 'daily')) {
return 600;
}
if (str_contains($message, 'ten secondly')) {
return 10;
}
if (str_contains($message, 'secondly')) {
return 1;
}
$this->log->warning('[Hubspot] No retry-after header or known message, using default', [
'exception_class' => get_class($e),
'message' => $message,
]);
return 10;
}
public function parseMessage(Throwable $e): string
{
if (! method_exists($e, 'getResponseBody')) {
return '';
}
$body = $e->getResponseBody();
if (is_string($body)) {
$body = json_decode($body, true) ?? [];
}
if (! is_array($body)) {
return '';
}
return (string) ($body['message'] ?? '');
}
public function getMinimumApiVersion(): string
{
return self::MIN_API_VERSION;
}
public function getInstance(): Factory
{
return new Factory([
'key' => $this->accessToken,
'oauth2' => true,
'base_url' => $this->baseUrl,
]);
}
public function getNewInstance(): Discovery
{
return \HubSpot\Factory::createWithAccessToken($this->accessToken);
}
/**
* Secondly and daily limits for Hubspot API
*
* Product Tier: Free & Starter | Professional & Enterprise | API add-on (any tier)
* Burst: 100/10 seconds | 150/10 seconds | 200/10 seconds
* Daily: 250,000 | 500,000 | 1,000,000
*
* Official documentation states: The search endpoints are rate limited to five requests per second.
* Since with 5 RPS were still hitting secondly rate limits we lowered it to 4
*/
public function getPaginatedData(array $payload, string $type, int $offset = 0): array
{
$total = 0;
$lastId = null;
$rows = [];
foreach ($this->getPaginatedDataGenerator($payload, $type, $offset, $total, $lastId) as $row) {
$rows[] = $row;
}
return ['results' => $rows, 'total' => $total, 'last_record' => $lastId];
}
/**
* @throws HubspotException
* @throws SocialAccountTokenInvalidException
* @throws BadRequest
*/
public function getPaginatedDataGenerator(
array $payload,
string $type,
int $offset = 0,
int &$total = 0,
?string &$lastRecordId = null
): \Generator {
return $this->paginationService->getPaginatedDataGenerator(
$this,
$payload,
$type,
$offset,
$total,
$lastRecordId
);
}
/**
* Execute a search request against HubSpot CRM objects with rate limiting.
*
* @param string $objectType The object type ('deals', 'companies', 'contacts', 'calls')
* @param array<string, mixed> $payload The search payload with filters, sorts, properties, etc.
* @return array The search response with 'results', 'total', 'paging' keys
* @throws RateLimitException When rate limit is hit
* @throws HubspotException On API errors
*/
public function search(string $objectType, array $payload): array
{
$endpoint = self::BASE_URL . "/crm/v3/objects/{$objectType}/search";
return $this->executeRequest(function () use ($endpoint, $payload) {
$response = $this->getInstance()->getClient()->request('POST', $endpoint, ['json' => $payload]);
return $response->toArray();
});
}
/**
* @throws DealApiException
* @throws CrmException
*/
public function getOpportunityById(string $crmId, array $fields): array
{
try {
// $deal = $this->executeRequest(fn () => $this->getNewInstance()->crm()->deals()->basicApi()->getById(
$deal = $this->getNewInstance()->crm()->deals()->basicApi()->getById(
$crmId,
implode(',', $fields),
'companies,contacts'
);
} catch (DealApiException $e) {
$this->log->info('[Hubspot] Failed to fetch opportunity', [
'crm_id' => $crmId,
'reason' => $e->getMessage(),
]);
throw $e;
}
if (! $deal instanceof DealWithAssociations) {
throw new CrmException('Deal not found');
}
return [
'id' => $deal->getId(),
'properties' => $deal->getProperties(),
'associations' => $deal->getAssociations(),
];
}
/**
* Generic batch read method for HubSpot objects
*
* @param string $objectType The object type ('deals', 'companies', 'contacts')
* @param array<string> $crmIds Array of HubSpot object IDs (max 100)
* @param array<string> $fields Array of property names to fetch
*
* @return array<string, array> Array keyed by CRM ID with object data
*/
private function batchReadObjects(string $objectType, array $crmIds, array $fields): array
{
if (empty($crmIds)) {
return [];
}
$this->validateBatchSize($objectType, $crmIds);
$this->ensureValidToken();
try {
$batchConfig = $this->createBatchConfiguration($objectType);
$batchReadRequest = $this->prepareBatchRequest($batchConfig, $crmIds, $fields);
$response = $batchConfig['api']->read($batchReadRequest);
$this->validateApiResponse($response, $objectType);
$results = $this->processApiResults($response);
$this->logBatchResults($objectType, $crmIds, $results);
return $results;
} catch (\Throwable $e) {
$this->handleBatchError($e, $objectType, $crmIds);
}
}
private function validateBatchSize(string $objectType, array $crmIds): void
{
if (count($crmIds) > 100) {
throw new \InvalidArgumentException("Batch size cannot exceed 100 {$objectType}");
}
}
private function createBatchConfiguration(string $objectType): array
{
$configurations = [
'deals' => [
'batchReadRequest' => new \HubSpot\Client\Crm\Deals\Model\BatchReadInputSimplePublicObjectId(),
'inputClass' => \HubSpot\Client\Crm\Deals\Model\SimplePublicObjectId::class,
'api' => $this->getNewInstance()->crm()->deals()->batchApi(),
],
'companies' => [
'batchReadRequest' => new \HubSpot\Client\Crm\Companies\Model\BatchReadInputSimplePublicObjectId(),
'inputClass' => \HubSpot\Client\Crm\Companies\Model\SimplePublicObjectId::class,
'api' => $this->getNewInstance()->crm()->companies()->batchApi(),
],
'contacts' => [
'batchReadRequest' => new \HubSpot\Client\Crm\Contacts\Model\BatchReadInputSimplePublicObjectId(),
'inputClass' => \HubSpot\Client\Crm\Contacts\Model\SimplePublicObjectId::class,
'api' => $this->getNewInstance()->crm()->contacts()->batchApi(),
],
];
if (! isset($configurations[$objectType])) {
throw new \InvalidArgumentException("Unsupported object type: {$objectType}");
}
return $configurations[$objectType];
}
private function prepareBatchRequest(array $batchConfig, array $crmIds, array $fields): object
{
$batchReadRequest = $batchConfig['batchReadRequest'];
$inputClass = $batchConfig['inputClass'];
$inputs = array_map(function ($crmId) use ($inputClass) {
$input = new $inputClass();
$input->setId($crmId);
return $input;
}, $crmIds);
$batchReadRequest->setInputs($inputs);
$batchReadRequest->setProperties($fields);
return $batchReadRequest;
}
private function validateApiResponse($response, string $objectType): void
{
if (! $response) {
throw new CrmException("HubSpot API returned null response for {$objectType} batch read");
}
}
private function processApiResults($response): array
{
$results = [];
$responseResults = $response->getResults();
if ($responseResults) {
foreach ($responseResults as $object) {
if ($object && $object->getId()) {
$results[$object->getId()] = [
'id' => $object->getId(),
'properties' => $object->getProperties() ?: [],
];
}
}
}
return $results;
}
private function logBatchResults(string $objectType, array $crmIds, array $results): void
{
$this->log->info("[HubSpot] Batch fetched {$objectType}", [
'requested_count' => count($crmIds),
'returned_count' => count($results),
'crm_ids' => $crmIds,
]);
}
private function handleBatchError(\Throwable $e, string $objectType, array $crmIds): void
{
$errorMessage = $e->getMessage() ?: 'Unknown error';
$errorTrace = $e->getTraceAsString() ?: 'No trace available';
$this->log->error("[HubSpot] Failed to batch fetch {$objectType}", [
'crm_ids' => $crmIds,
'error' => $errorMessage,
'trace' => $errorTrace,
]);
throw new CrmException("Failed to batch fetch {$objectType}: " . $errorMessage);
}
/**
* Batch read multiple opportunities by their CRM IDs
*
* @param array<string> $crmIds Array of HubSpot deal IDs (max 100)
* @param array<string> $fields Array of property names to fetch
*
* @return array<string, array> Array keyed by CRM ID with opportunity data
*/
public function getOpportunitiesByIds(array $crmIds, array $fields): array
{
return $this->batchReadObjects('deals', $crmIds, $fields);
}
/**
* Batch read multiple companies by their CRM IDs
*
* @param array<string> $crmIds Array of HubSpot company IDs (max 100)
* @param array<string> $fields Array of property names to fetch
*
* @return array<string, array> Array keyed by CRM ID with company data
*/
public function getCompaniesByIds(array $crmIds, array $fields): array
{
return $this->batchReadObjects('companies', $crmIds, $fields);
}
/**
* Batch read multiple contacts by their CRM IDs
*
* @param array<string> $crmIds Array of HubSpot contact IDs (max 100)
* @param array<string> $fields Array of property names to fetch
*
* @return array<string, array> Array keyed by CRM ID with contact data
*/
public function getContactsByIds(array $crmIds, array $fields): array
{
return $this->batchReadObjects('contacts', $crmIds, $fields);
}
/**
* @throws CompanyApiException
* @throws CrmException
*/
public function getAccountById(string $crmId, array $fields): array
{
try {
$company = $this->getNewInstance()->crm()->companies()->basicApi()->getById(
$crmId,
implode(',', $fields),
);
} catch (CompanyApiException $e) {
$this->log->info('[Hubspot] Failed to fetch account', [
'crm_id' => $crmId,
'reason' => $e->getMessage(),
]);
throw $e;
}
if (! $company instanceof CompaniesWithAssociations) {
throw new CrmException('Account not found');
}
return [
'id' => $company->getId(),
'properties' => $company->getProperties(),
];
}
/**
* @throws ContactApiException
* @throws CrmException
*/
public function getContactById(string $crmId, array $fields): array
{
try {
$contact = $this->getNewInstance()->crm()->contacts()->basicApi()->getById(
$crmId,
implode(',', $fields)
);
} catch (ContactApiException $e) {
$this->log->info('[Hubspot] Failed to fetch contact', [
'crm_id' => $crmId,
'reason' => $e->getMessage(),
]);
throw $e;
}
if (! $contact instanceof ContactsWithAssociations) {
throw new CrmException('Contact not found');
}
return [
'id' => $contact->getId(),
'properties' => $contact->getProperties(),
];
}
/**
* This is email search request that Hubspot offers as GET (more generous quota)
*/
public function getContactByEmail(string $email, array $fields = []): array
{
try {
$contact = $this->getNewInstance()->crm()->contacts()->basicApi()->getById(
$email,
implode(',', $fields),
null,
false,
'email'
);
return [
'id' => $contact->getId(),
'properties' => $contact->getProperties(),
];
} catch (ContactApiException $e) {
$this->log->info('[Hubspot] Failed to fetch contact', [
'email' => $email,
'reason' => $e->getMessage(),
]);
return [];
}
}
/**
* @throws CrmException
*/
public function fetchProperty(string $objectType, string $propertyId): Property
{
$result = $this->getNewInstance()->crm()->properties()->coreApi()->getByName($objectType, $propertyId);
if (! $result instanceof Property) {
$this->log->error('[Hubspot] Failed to fetch property', [
'object_type' => $objectType,
'property_id' => $propertyId,
'reason' => $result->getMessage(),
]);
throw new CrmException('Failed to fetch property');
}
return $result;
}
/**
* @return array<CrmFieldOption>
*/
public function fetchPropertyOptions(string $objectType, string $propertyId): array
{
/** @var array<CrmFieldOption> */
return $this->fetchProperty($objectType, $propertyId)->getOptions();
}
/**
* @return array<array{id:string, label:string, deleted:bool}>
*/
public function fetchCallDispositions(): array
{
/** @var Response $response */
$response = $this->getInstance()->engagements()->getCallDispositions();
/**
* @var array<array{
* id:string,
* label:string,
* deleted: bool
* }>
*/
return $response->toArray();
}
/**
* @return array<CrmFieldOption>
*/
public function fetchOpportunityPipelineStages(): array
{
$stages = [];
$apiResponse = $this->getNewInstance()->crm()->pipelines()->pipelinesApi()->getAll('deals');
if ($apiResponse instanceof Error) {
$this->log->error('[Hubspot] Failed to fetch opportunity pipelines', [
'reason' => $apiResponse->getMessage(),
]);
return [];
}
foreach ($apiResponse->getResults() as $pipeline) {
$pipelineStages = array_map(
static function (PipelineStage $stage) {
return [
'id' => $stage->getId(),
'label' => $stage->getLabel(),
];
},
$pipeline->getStages()
);
$stages = array_merge($stages, $pipelineStages);
}
return $stages;
}
public function fetchOpportunityPipelines(): array
{
$pipelines = [];
try {
$apiResponse = $this->makeRequest('/crm/v3/pipelines/deals');
} catch (\Exception $e) {
$this->log->info('[Hubspot] Failed to fetch opportunity pipelines', [
'reason' => $e->getMessage(),
]);
return [];
}
$response = $apiResponse->toArray();
foreach ($response['results'] as $pipeline) {
$pipelines[] = [
'id' => $pipeline['id'],
'label' => $pipeline['label'],
];
}
return $pipelines;
}
/**
* @return array<CrmFieldOption>
*/
public function fetchMeetingOutcomeFieldOptions(Field $field): array
{
return $field->getCrmProviderId() === 'meetingOutcome'
? $this->fetchMeetingOutcomeTypes()
: $this->fetchCallActivityTypes();
}
public function fetchMeetingOutcomeTypes(): array
{
return $this->extractMeetingTypeOptions(
'[URL_WITH_CREDENTIALS] Response $response */
$response = $this->getInstance()
->getClient()
->request('GET', $endpoint);
/**
* @var array<array{
* value: string,
* label: string,
* displayOrder: int
* }> $optionData
*/
$optionData = $response->toArray()['options'] ?? [];
$options = [];
foreach ($optionData as $item) {
$options[] = [
'id' => $item['value'],
'value' => $item['value'],
'label' => $item['label'],
'display_order' => $item['displayOrder'],
];
}
return $options;
}
/**
* @return array<CrmFieldOption>
*/
public function fetchDispositionFieldOptions(): array
{
$options = [];
$dispositions = $this->fetchCallDispositions();
foreach ($dispositions as $disposition) {
if ($disposition['deleted'] !== false) {
continue;
}
$option['value'] = $disposition['id'];
$option['id'] = $disposition['id'];
$option['label'] = $disposition['label'];
$options[] = $option;
}
return $options;
}
/**
* @return array<CrmFieldOption>
*/
public function fetchOpportunityFieldOptions(Field $field): array
{
if ($field->isStageField()) {
return $this->fetchOpportunityPipelineStages();
}
if ($field->isPipelineField()) {
return $this->fetchOpportunityPipelines();
}
return $this->fetchPropertyOptions('deals', $field->getCrmProviderId());
}
/**
* @throws BadRequest
* @throws HubspotException
*/
public function makeRequest(string $endpoint, $method = 'GET', $payload = [], ?string $queryString = null)
{
$endpoint = self::BASE_URL . $endpoint;
if ($method === 'GET') {
return $this->getInstance()->getClient()?->request(
method: $method,
endpoint: $endpoint,
query_string: $queryString
);
} else {
return $this->getInstance()->getClient()->request($method, $endpoint, [
'json' => ($payload),
]);
}
}
/**
* @throws BadRequest
* @throws HubspotException
*/
public function createMeeting(array $payload): Response
{
$endpoint = '/crm/v3/objects/meetings';
return $this->makeRequest($endpoint, 'POST', $payload);
}
/**
* @throws BadRequest
* @throws HubspotException
*/
public function updateMeeting(string $meetingId, array $payload): Response
{
$endpoint = '/crm/v3/objects/meetings/' . $meetingId;
return $this->makeRequest($endpoint, 'PATCH', $payload);
}
/**
* @throws \Exception
*/
public function createNote(
string $body,
string $ownerId,
int $timestamp,
string $objectId,
NoteObject $noteObject
): ?string {
try {
$noteInput = new SimplePublicObjectInput([
'properties' => [
'hs_note_body' => $body,
'hubspot_owner_id' => $ownerId,
'hs_timestamp' => $timestamp,
],
]);
// Create note
$note = $this->getNewInstance()->crm()->objects()->basicApi()->create('note', $noteInput);
$this->getNewInstance()->crm()->objects()->associationsApi()->create(
'note',
$note->getId(),
$this->getNoteObject($noteObject),
$objectId,
$this->getNoteAssociationType($noteObject),
);
return $note->getId();
} catch (\Exception $e) {
$this->log->error('[Hubspot] Failed to create note', [
'objectId' => $objectId,
'noteObject' => $noteObject->getObjectType(),
'reason' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
return null;
}
public function updateEngagement(string $objectId, array $engagement, array $metadata): void
{
$this->getInstance()->engagements()->update($objectId, $engagement, $metadata);
}
public function getEngagementData(string $engagementId): array
{
$engagement = $this->getInstance()->engagements()->get($engagementId);
return $engagement->toArray();
}
public function createEngagement(array $engagement, array $associations, array $metadata): Response
{
return $this->getInstance()
->engagements()
->create($engagement, $associations, $metadata);
}
public function isUnauthorizedException(\Exception $e): bool
{
// Check for specific HubSpot API exception types first
if ($e instanceof BadRequest) {
// BadRequest can contain 401 status codes
return $e->getCode() === 401;
}
// Check for HTTP client exceptions with status codes
if ($e instanceof \GuzzleHttp\Exception\RequestException && $e->hasResponse()) {
$response = $e->getResponse();
if ($response !== null) {
return $response->getStatusCode() === 401;
}
}
// Check for Guzzle HTTP exceptions
if ($e instanceof \GuzzleHttp\Exception\ClientException) {
return $e->getCode() === 401;
}
// Fallback to string matching as last resort, but be more specific
$message = strtolower($e->getMessage());
return str_contains($message, '401 unauthorized') ||
str_contains($message, 'http 401') ||
str_contains($message, 'status code 401') ||
(preg_match('/\b401\b/', $message) && str_contains($message, 'unauthorized'));
}
/**
* Validates and refreshes the access token if needed before API requests.
* This ensures long-running processes don't fail due to token expiration.
*
* @throws SocialAccountTokenInvalidException
*/
public function ensureValidToken(): void
{
if ($this->oauthAccount === null) {
return;
}
$newToken = $this->tokenManager->ensureValidToken($this->oauthAccount);
if ($newToken !== null) {
$this->accessToken = $newToken;
}
}
public function getConfig()
{
return $this->config;
}
// returns only active (archived=false)
public function getOwners(): array
{
return $this->getNewInstance()->crm()->owners()->getAll();
}
/**
* @param bool $archived
*
* @return array<Owner>|[]
*/
public function getOwnersArchived(bool $archived = true): array
{
$endpoint = '/crm/v3/owners';
$queryParams = [
'archived' => $archived ? 'true' : 'false',
];
$queryString = http_build_query($queryParams);
$owners = [];
try {
$response = $this->makeRequest(endpoint: $endpoint, queryString: $queryString);
$responseData = $response?->toArray();
foreach ($responseData['results'] as $result) {
try {
$owners[] = Owner::create($result);
} catch (Throwable $e) {
$this->log->error('[HubSpot] Failed to process owner data', [
'result' => $result,
'error' => $e->getMessage(),
]);
continue;
}
}
} catch (Throwable $e) {
$this->log->error('HubSpot] Failed to fetch owners', [
'archived' => $archived,
'error' => $e->getMessage(),
]);
return [];
}
return $owners;
}
public function getMeeting(string $engagementId): ObjectWithAssociations
{
return $this->getNewInstance()->crm()->objects()->basicApi()
->getById('meeting', $engagementId, null, 'contact,company,deal');
}
public function deleteEngagement(string $engagementId): void
{
$this->getInstance()->engagements()->delete((int) $engagementId);
}
public function getAssociationsData(array $ids, string $fromObject, string $toObject): array
{
$associationData = [];
$idChunks = array_chunk($ids, self::ASSOCIATIONS_BATCH_SIZE_LIMIT);
foreach ($idChunks as $idChunk) {
try {
$batchInput = new \HubSpot\Client\Crm\Associations\Model\BatchInputPublicObjectId();
$batchInput->setInputs(array_map(function ($id) {
$publicObjectId = new \HubSpot\Client\Crm\Associations\Model\PublicObjectId();
$publicObjectId->setId($id);
return $publicObjectId;
}, $idChunk));
$associatedObjectsData = $this
->getNewInstance()
->crm()
->associations()
->batchApi()
->read($fromObject, $toObject, $batchInput);
if ($associatedObjectsData instanceof \HubSpot\Client\Crm\Associations\Model\BatchResponsePublicAssociationMulti) {
foreach ($associatedObjectsData->getResults() as $association) {
$from = $association->getFrom()->getId();
$toAssociations = $association->getTo();
if (! empty($toAssociations)) {
$associationData[$from] = array_map(function ($item) {
return $item->getId();
}, $toAssociations);
}
}
}
// } catch (RateLimitException $e) {
// throw $e;
} catch (\Exception $e) {
$this->log->error('[Hubspot] Failed to fetch associations', [
'from_object' => $fromObject,
'to_object' => $toObject,
'reason' => $e->getMessage(),
]);
}
}
return $associationData;
}
/**
* @throws \Exception
*/
private function getNoteAssociationType(NoteObject $noteObject): string
{
return match($noteObject) {
NoteObject::Opportunity => 'note_to_deal',
NoteObject::Lead, NoteObject::Contact => 'note_to_contact', // or 'note_to_lead' if your portal supports it
NoteObject::Account => 'note_to_company',
NoteObject::Call, NoteObject::Event => throw new \Exception('Not supported'),
};
}
/**
* @throws \Exception
*/
private function getNoteObject(NoteObject $noteObject): string
{
return match($noteObject) {
NoteObject::Opportunity => 'deal',
NoteObject::Lead, NoteObject::Contact => 'contact',
NoteObject::Account => 'company',
NoteObject::Call, NoteObject::Event => throw new \Exception('Not supported'),
};
}
public function addAssociations(string $objectType, string $associationType, array $payload): Response
{
$endpoint = "/crm/v4/associations/$objectType/$associationType/batch/create";
return $this->makeRequest($endpoint, 'POST', $payload);
}
public function removeAssociations(string $objectType, string $associationType, array $payload): Response
{
$endpoint = "/crm/v4/associations/$objectType/$associationType/batch/archive";
return $this->makeRequest($endpoint, 'POST', $payload);
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
19
Previous Highlighted Error...
|
18192
|
NULL
|
NULL
|
NULL
|
|
18195
|
790
|
4
|
2026-05-11T11:03:42.895001+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-11/1778 /Users/lukas/.screenpipe/data/data/2026-05-11/1778497422895_m1.jpg...
|
PhpStorm
|
faVsco.js – HandleHubspotRateLimit.php
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
8043719072324535154
|
-8628527368849355612
|
click
|
hybrid
|
NULL
|
Project: faVsco.js, menu
SlackFileEditViewGoHistor Project: faVsco.js, menu
SlackFileEditViewGoHistoryWindowHelpDOCKER₴81DEV (docker)$2APP (-zsh)883viewsjiminny-worker-processing-2:jiminny-worker-processing-2_00: stoppedjiminny-worker-processing-3:jiminny-worker-processing-3_00: stoppedjiminny-worker-processing-4: jiminny-worker-processing-4_00: stoppedJiminny-worker-processing-5:j1minny-worker-processing-5_00:jiminny-worker-processing-delayed: jiminny-worker-processing-delayed_00: stoppedworker-analytics:worker-analytics_00: stoppedworker-conferences:worker-conferences_00: stoppedworker-crm-update:worker-crm-update_00: stoppedworker-download:worker-download_00: stoppedworker-emails:worker-emails_00: stoppedworker-nudges:worker-nudges_00: stoppedworker:worker_00: stoppedworker-audio:worker-audio_00: stoppedworker-calendar:worker-calendar_00: stoppedworker-crm-sync:worker-crm-sync_00: stoppedartisan-schedule:artisan-schedule_00: stoppedworker-es-update:worker-es-update_00: stoppedjiminny-worker-processing-1:jiminny-worker-processing-1_00: stoppedartisan-schedule:artisan-schedule_00: startedjiminny-worker-processing-1:jiminny-worker-processing-1_00: startedjiminny-worker-processing-2:jiminny-worker-processing-2_00: startedjiminny-worker-processing-3:jiminny-worker-processing-3_00: startedjiminny-worker-processing-4:jiminny-worker-processing-4_00: startedjiminny-worker-processing-5:jiminny-worker-processing-5_00:jiminny-worker-processing-delayed: jiminny-worker-processing-delayed_00: startedworker:worker_00: startedworker-analytics:worker-analytics_00: startedworker-audio:worker-audio_00: startedworker-calendar:worker-calendar_00: startedworker-conferences:worker-conferences_00: startedworker-crm-sync:worker-crm-sync_00: startedworker-crm-update:worker-crm-update_00: startedworker-download:worker-download_00: startedworker-emails:worker-emails_00: startedworker-es-update:worker-es-update_00: startedworker-nudges:worker-nudges_00: startedroot@docker_lamp_1:/home/jiminny#php artisan jiminny:debugDispatching 100 MatchActivityCrmData jobs (portal=2)Done. Watch logs and run jiminny:debug observeRateLimit to inspect cache state.root@docker_lamp_1:/home/jiminny# php artisan jiminny:debugDispatching 100 MatchActivityCrmData jobs (portal=2)Done. Watch logs and run jiminny:debug observeRateLimit to inspect cache state.root@docker_lamp_1:/home/jiminny# ]DEV (d)-zsh• .HomeDMsActivityLater...liholg Preparation for Refi... in 57 m100% <78• Mon 11 May14:03:42→Describe what you are looking forJiminny ...# contusion-clinic# curiosity_lab# engineering# general# jiminny-bg# platform-tickets# product_launches# random# releases# sofia-office# support# thank-yous# the_people_of jimi...ToastHomeMessages^ Direct messages®. Aneliya Angelova®. Galya Dimitrova I& Petko Kashinski&. Stefka StoyanovaVasil Vasilev&. Nikolay IvanovAneliya Angelova, ...Stoyan Tanev®. VesE Lukas Kovalik y... 0AboutiminnyFriday, May 8th ~Added by Toast for GitHubToday ~Toast APP10:00 AMReviewapp#12059 Jy 20820 es reindexstream model hydration2 days old • 12 files changed •@Vasil VasilevAdded by Toast for GitHubResolve Conflictsapp#11443 Test hublets latency5 months old • 20 files changed#11327 JY-19501 webhookbased opportunity syncShow moreAdded by Toast for GitHubNeeds Loveapp#12024 JY-20773 fix user pilottracking ofr automated reportgenerated12 days old • 1 file changedE:: AppsMessage ToastJira CloudToast+AaConala CalaNew...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
18196
|
791
|
3
|
2026-05-11T11:03:42.871207+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-11/1778 /Users/lukas/.screenpipe/data/data/2026-05-11/1778497422871_m2.jpg...
|
PhpStorm
|
faVsco.js – HandleHubspotRateLimit.php
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
PhostormVIewINavicareCodeWindowFV faVsco.js°9 JY-2 PhostormVIewINavicareCodeWindowFV faVsco.js°9 JY-20725-handle-HS-search-rate-limiProiect© BatchSyncCollectolhuospot/service.onpT SyncCrmEntitiesTrait.phpc) CachedCrmServiceDecorator.ongJiminnyDeougcommana.onge balchsynckealssec clientoneo closeaDealstagesso RematchActivityonCrmObiectDetach.pngDealrielasservice.gRateLimitexcedc)Decorateacuivilv.or© FieldDefinitions.phrC) CrmObiectsResolver.pho() ProviderRateLimiter.phpC) PaqinationContia.phpC) FieldT vpeconverteclass Cuient extends BasecLient imolements Hubspotcuientinterfacee Hubspotclientinterc) HubspotlokenmanC) PavloadBuilder.phpC) RemotecrmobiectrP ResponseNormalize210 Cc) Service.onrC)SvncFieldAction.onC) SvncRelatedActivitC) WebhookSvncBatclv MintearationAorM Acceccors•MAnConfigD DTO• M Silters219D Jobs• M ProcnectSoarchStr.W service lraits© DataClient.php© DecorateActivity.ph(e) LocalSearch.onpu Localsearchinteria227 C© RemoteSearch.phpc) Service.phpv W Listeners© ConvertLeadActivitc) PurceLookuocache> M Metadata> MiarationiPioedrivev Salesforce236• D FieldsM OnnortunitvMatcheMOnnortunitvSvneStl239M ProsneetSearchStr.• M ServiceTraitcC) Client nhr© DecorateActivity.pr. Delete@biectsTrait© FieldDefinitions.php249© PayloadBuilder.php© Profile.php© QueryBuilder.php248Tacts nacced. 12 (1 hour adoi* Ufficial documentation states: The search endpoints are rate limited to five requests per second.* Since with 5 RPS were still hitting secondly rate limits we lowered it to 4public function getPaginatedData(array Spayload, string $type, int $offset = 0): arrayStotal = 0;Sastid = nun•Srows = []:foreach (Sthis->getPaginatedDataGenerator(Spayload, $type, $offset,8: Stotal.QlastRecordld: Slas+tdh as Srow&Srows(] = $rowreturn ['results' => $rows, 'total' => $total, 'last_record' => $lastId]:*acows nuospoccxceotlon*dchrows soc1alaccountlokenLnval1dzxcept1on* dchrows Badkequestpublic function getPaginatedDataGenerator(arrav soavload.lstrina Stvoelint Soffset = 0.int &stotal = 0.Ostrina &SlastRecordid = nuluGenerator "neturnsthis->naginationServi.ce->aetpaginatedbataGeneraton(SthisSnavload.$type,Soffset.8: Stotal..8: SlactPecordTd• Execute a search request against HubSpot CRM obiects with rate limiting.* Anaram string SobjectType The object tupe ('deals'.'contacts', 'calls')* anaram array<string, mixed> Spayload The search payload with filters, sorts, properties. etc.A6 VAYPreparation tor kell... In o/ m100% 2• Mon 11 May 14:03:42HandleHubspotRateLimitTest v= custom.loa X|4 HS_local (iminny@localhost]# console [PKoD)CascadeA console (EU]A console [STAGING]Hubspot Rate Limit Re+0 ..[2026-05-07 14:21:15] local.INF0: [Hubspot] DEBUG Getting headers {w19Alets simplify it, just call it parseMessage and skip policy al together"Vace".L"Inu,or may 20z0 14.21.10 bMl"J,Thought for 5s ›Read Client.php #L86-200"Connection":"keep-aLive".rb Client.php+17 -35"CF-Ray":"9t80deb8dbo0dcsa-S0F"arVerity nothing broke:Command docker. tail"Strict-Transport-Security":"max-aqe=31536000* 1ncLudesubbomains: preload")."server-timing": ["hcid;desc=\"019e02d0-6fd8-7812-bdba-885b7ccb3ee3)",docker execetests/Unit/Jobs/Middleware/HandleHubspotRateLimitTest-phptests/Unit/Services/Crm/Hubspot/ClientTest.php --no-coverage 2>&1 | tail -10pup92.20contzaeration: Phomes3.minnv/ohpunit.xml"x-hubspot-correlation-id":["019e02d0-6fd8-7812-bdba-885b7ccb3ee3"),"Set-Cookie":["__cf_bm=S1UrtdQgXVrik50pdqF6hZVYKhzTnQBidvMabeCtm0Y-1778163675-[IP_ADDRESS]-rI.ZggtDKxTg07-May-26 14:51:15 GMT; domain=.hubapi.com; Http0nly; Secure; SameSite=None"],"Renont-Tol.":D.:Time: 02:14.415. Memorv: 62.00 MB•[PASSWORD_DOTS]DD..DD..D.D..DDDDD.DDD......65 / 80 (1808)OK hut there were ¿csuec!All 80 tests still passN"url\":\"https:|\/\\/a.nel.cloudflare.com\/report\\/v4?s=NYAlsVTP0fYm32qrSDjxYE4sd2RWRqiSp3wHsr\"group\":\"cf-nel\",\"max_age\":604800}"],"NEL": ["{\"success_fraction\":0.01,reporc to. "cr-nel,"max age":604800}"]"Server": ["cloudflare"]}} {"correlation id":"95236535-ec98-4541-b92a-adfa73b69eab".SummaryRemoved narsePo Eicv entirelv. narseRetrvAfter now mans messade kevwords direct v to seconds without theintermediate policy conceptBefore: header a narsePolicy a nolicy constant a seconds After: header « narceMessage „ secondsChanges:•parsePoLicy → deleted•narseMessade added: returns the raw messade strina from the response bodv (or .. )•parseRetryAfter does the keyword matching itself (daily → 600, ten secondly → 10, secondly → 1)I"trace_1d":"C/aD8565-905t-4604-9405-0e5b551e5545'• Log line in executeRequest now logs message instead of policy - more informative for debuggingThe keyword-match order is the same as before (daily before ten secondly before secondly ) to avoid substringcollision.IІПII 1 1IIIIlII111 1.nl .Ask anvthina (84L)« CodeClaude Onus 4.7 MediumWN Windsurf Toams 228-24UTF.8io 4 spaces...
|
NULL
|
-8032017939677384184
|
NULL
|
click
|
ocr
|
NULL
|
PhostormVIewINavicareCodeWindowFV faVsco.js°9 JY-2 PhostormVIewINavicareCodeWindowFV faVsco.js°9 JY-20725-handle-HS-search-rate-limiProiect© BatchSyncCollectolhuospot/service.onpT SyncCrmEntitiesTrait.phpc) CachedCrmServiceDecorator.ongJiminnyDeougcommana.onge balchsynckealssec clientoneo closeaDealstagesso RematchActivityonCrmObiectDetach.pngDealrielasservice.gRateLimitexcedc)Decorateacuivilv.or© FieldDefinitions.phrC) CrmObiectsResolver.pho() ProviderRateLimiter.phpC) PaqinationContia.phpC) FieldT vpeconverteclass Cuient extends BasecLient imolements Hubspotcuientinterfacee Hubspotclientinterc) HubspotlokenmanC) PavloadBuilder.phpC) RemotecrmobiectrP ResponseNormalize210 Cc) Service.onrC)SvncFieldAction.onC) SvncRelatedActivitC) WebhookSvncBatclv MintearationAorM Acceccors•MAnConfigD DTO• M Silters219D Jobs• M ProcnectSoarchStr.W service lraits© DataClient.php© DecorateActivity.ph(e) LocalSearch.onpu Localsearchinteria227 C© RemoteSearch.phpc) Service.phpv W Listeners© ConvertLeadActivitc) PurceLookuocache> M Metadata> MiarationiPioedrivev Salesforce236• D FieldsM OnnortunitvMatcheMOnnortunitvSvneStl239M ProsneetSearchStr.• M ServiceTraitcC) Client nhr© DecorateActivity.pr. Delete@biectsTrait© FieldDefinitions.php249© PayloadBuilder.php© Profile.php© QueryBuilder.php248Tacts nacced. 12 (1 hour adoi* Ufficial documentation states: The search endpoints are rate limited to five requests per second.* Since with 5 RPS were still hitting secondly rate limits we lowered it to 4public function getPaginatedData(array Spayload, string $type, int $offset = 0): arrayStotal = 0;Sastid = nun•Srows = []:foreach (Sthis->getPaginatedDataGenerator(Spayload, $type, $offset,8: Stotal.QlastRecordld: Slas+tdh as Srow&Srows(] = $rowreturn ['results' => $rows, 'total' => $total, 'last_record' => $lastId]:*acows nuospoccxceotlon*dchrows soc1alaccountlokenLnval1dzxcept1on* dchrows Badkequestpublic function getPaginatedDataGenerator(arrav soavload.lstrina Stvoelint Soffset = 0.int &stotal = 0.Ostrina &SlastRecordid = nuluGenerator "neturnsthis->naginationServi.ce->aetpaginatedbataGeneraton(SthisSnavload.$type,Soffset.8: Stotal..8: SlactPecordTd• Execute a search request against HubSpot CRM obiects with rate limiting.* Anaram string SobjectType The object tupe ('deals'.'contacts', 'calls')* anaram array<string, mixed> Spayload The search payload with filters, sorts, properties. etc.A6 VAYPreparation tor kell... In o/ m100% 2• Mon 11 May 14:03:42HandleHubspotRateLimitTest v= custom.loa X|4 HS_local (iminny@localhost]# console [PKoD)CascadeA console (EU]A console [STAGING]Hubspot Rate Limit Re+0 ..[2026-05-07 14:21:15] local.INF0: [Hubspot] DEBUG Getting headers {w19Alets simplify it, just call it parseMessage and skip policy al together"Vace".L"Inu,or may 20z0 14.21.10 bMl"J,Thought for 5s ›Read Client.php #L86-200"Connection":"keep-aLive".rb Client.php+17 -35"CF-Ray":"9t80deb8dbo0dcsa-S0F"arVerity nothing broke:Command docker. tail"Strict-Transport-Security":"max-aqe=31536000* 1ncLudesubbomains: preload")."server-timing": ["hcid;desc=\"019e02d0-6fd8-7812-bdba-885b7ccb3ee3)",docker execetests/Unit/Jobs/Middleware/HandleHubspotRateLimitTest-phptests/Unit/Services/Crm/Hubspot/ClientTest.php --no-coverage 2>&1 | tail -10pup92.20contzaeration: Phomes3.minnv/ohpunit.xml"x-hubspot-correlation-id":["019e02d0-6fd8-7812-bdba-885b7ccb3ee3"),"Set-Cookie":["__cf_bm=S1UrtdQgXVrik50pdqF6hZVYKhzTnQBidvMabeCtm0Y-1778163675-[IP_ADDRESS]-rI.ZggtDKxTg07-May-26 14:51:15 GMT; domain=.hubapi.com; Http0nly; Secure; SameSite=None"],"Renont-Tol.":D.:Time: 02:14.415. Memorv: 62.00 MB•[PASSWORD_DOTS]DD..DD..D.D..DDDDD.DDD......65 / 80 (1808)OK hut there were ¿csuec!All 80 tests still passN"url\":\"https:|\/\\/a.nel.cloudflare.com\/report\\/v4?s=NYAlsVTP0fYm32qrSDjxYE4sd2RWRqiSp3wHsr\"group\":\"cf-nel\",\"max_age\":604800}"],"NEL": ["{\"success_fraction\":0.01,reporc to. "cr-nel,"max age":604800}"]"Server": ["cloudflare"]}} {"correlation id":"95236535-ec98-4541-b92a-adfa73b69eab".SummaryRemoved narsePo Eicv entirelv. narseRetrvAfter now mans messade kevwords direct v to seconds without theintermediate policy conceptBefore: header a narsePolicy a nolicy constant a seconds After: header « narceMessage „ secondsChanges:•parsePoLicy → deleted•narseMessade added: returns the raw messade strina from the response bodv (or .. )•parseRetryAfter does the keyword matching itself (daily → 600, ten secondly → 10, secondly → 1)I"trace_1d":"C/aD8565-905t-4604-9405-0e5b551e5545'• Log line in executeRequest now logs message instead of policy - more informative for debuggingThe keyword-match order is the same as before (daily before ten secondly before secondly ) to avoid substringcollision.IІПII 1 1IIIIlII111 1.nl .Ask anvthina (84L)« CodeClaude Onus 4.7 MediumWN Windsurf Toams 228-24UTF.8io 4 spaces...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
18197
|
790
|
5
|
2026-05-11T11:03:48.065088+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-11/1778 /Users/lukas/.screenpipe/data/data/2026-05-11/1778497428065_m1.jpg...
|
PhpStorm
|
faVsco.js – Client.php
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
JY-20725-handle-HS-search Project: faVsco.js, menu
JY-20725-handle-HS-search-rate-limit, menu
Start Listening for PHP Debug Connections
HandleHubspotRateLimitTest
Run 'HandleHubspotRateLimitTest'
Debug 'HandleHubspotRateLimitTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
2
67
3
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Services\Crm\Hubspot;
use HubSpot\Client\Crm\Deals\ApiException as DealApiException;
use HubSpot\Client\Crm\Contacts\ApiException as ContactApiException;
use HubSpot\Client\Crm\Companies\ApiException as CompanyApiException;
use HubSpot\Client\Crm\Contacts\Model\SimplePublicObjectWithAssociations as ContactsWithAssociations;
use HubSpot\Client\Crm\Companies\Model\SimplePublicObjectWithAssociations as CompaniesWithAssociations;
use HubSpot\Client\Crm\Deals\Model\SimplePublicObjectWithAssociations as DealWithAssociations;
use HubSpot\Client\Crm\Objects\Model\SimplePublicObjectInput;
use HubSpot\Client\Crm\Objects\Model\SimplePublicObjectWithAssociations as ObjectWithAssociations;
use HubSpot\Client\Crm\Pipelines\Model\Error;
use HubSpot\Client\Crm\Pipelines\Model\PipelineStage;
use HubSpot\Client\Crm\Properties\Model\Property;
use HubSpot\Discovery\Discovery;
use Jiminny\Exceptions\CrmException;
use Jiminny\Exceptions\RateLimitException;
use Jiminny\Exceptions\SocialAccountTokenInvalidException;
use Jiminny\Jobs\Crm\NoteObject;
use Jiminny\Models\Crm\Field;
use Jiminny\Services\Crm\BaseClient;
use Jiminny\Services\Crm\Hubspot\DTO\Response\Owner;
use Jiminny\Services\SocialAccountService;
use SevenShores\Hubspot\Exceptions\BadRequest;
use SevenShores\Hubspot\Exceptions\HubspotException;
use SevenShores\Hubspot\Factory;
use SevenShores\Hubspot\Http\Response;
use Jiminny\Services\Crm\Hubspot\Pagination\HubspotPaginationService;
use Illuminate\Support\Facades\Redis;
use Throwable;
/**
* @phpstan-type CrmFieldOption array{id:string, label:string, value?:string}
*/
class Client extends BaseClient implements HubspotClientInterface
{
public const string MIN_API_VERSION = '2';
public const string BASE_URL = '[URL_WITH_CREDENTIALS] T
* @param callable(): T $apiCall
* @return T
*
* @throws RateLimitException
*/
private function executeRequest(callable $apiCall)
{
$cacheKey = $this->getRateLimitCacheKey();
$cachedRetryAfter = Redis::get($cacheKey);
if (is_string($cachedRetryAfter) && is_numeric($cachedRetryAfter)) {
throw new RateLimitException(
'Hubspot rate limit (cached circuit-breaker)',
(int) $cachedRetryAfter,
);
}
try {
return $apiCall();
} catch (Throwable $e) {
if ($this->isHubspotRateLimit($e)) {
$retryAfter = $this->parseRetryAfter($e);
Redis::setex($cacheKey, $retryAfter, (string) $retryAfter);
$this->log->warning('[Hubspot] Received 429 from API', [
'team_id' => $this->config->team_id,
'config_id' => $this->config->getId(),
'retry_after' => $retryAfter,
'message' => $this->parseMessage($e),
'reason' => $e->getMessage(),
]);
throw new RateLimitException('Hubspot returned 429', $retryAfter, $e);
}
throw $e;
}
}
private function getRateLimitCacheKey(): string
{
return sprintf('hubspot:ratelimit:portal:%d', $this->config->getId());
}
public function isHubspotRateLimit(Throwable $e): bool
{
if ($e instanceof BadRequest
|| $e instanceof DealApiException
|| $e instanceof ContactApiException
|| $e instanceof CompanyApiException
|| $e instanceof \GuzzleHttp\Exception\RequestException
) {
return (int) $e->getCode() === 429;
}
return false;
}
public function parseRetryAfter(Throwable $e): int
{
if (method_exists($e, 'getResponseHeaders')) {
$headers = $e->getResponseHeaders() ?: [];
$value = $headers['Retry-After'] ?? $headers['retry-after'] ?? null;
if (is_array($value)) {
$value = $value[0] ?? null;
}
if (is_numeric($value)) {
return (int) $value;
}
}
$message = strtolower($this->parseMessage($e));
if (str_contains($message, 'daily')) {
return 600;
}
if (str_contains($message, 'ten secondly')) {
return 10;
}
if (str_contains($message, 'secondly')) {
return 1;
}
$this->log->warning('[Hubspot] No retry-after header or known message, using default', [
'exception_class' => get_class($e),
'message' => $message,
]);
return 10;
}
public function parseMessage(Throwable $e): string
{
if (! method_exists($e, 'getResponseBody')) {
return '';
}
$body = $e->getResponseBody();
if (is_string($body)) {
$body = json_decode($body, true) ?? [];
}
if (! is_array($body)) {
return '';
}
return (string) ($body['message'] ?? '');
}
public function getMinimumApiVersion(): string
{
return self::MIN_API_VERSION;
}
public function getInstance(): Factory
{
return new Factory([
'key' => $this->accessToken,
'oauth2' => true,
'base_url' => $this->baseUrl,
]);
}
public function getNewInstance(): Discovery
{
return \HubSpot\Factory::createWithAccessToken($this->accessToken);
}
/**
* Secondly and daily limits for Hubspot API
*
* Product Tier: Free & Starter | Professional & Enterprise | API add-on (any tier)
* Burst: 100/10 seconds | 150/10 seconds | 200/10 seconds
* Daily: 250,000 | 500,000 | 1,000,000
*
* Official documentation states: The search endpoints are rate limited to five requests per second.
* Since with 5 RPS were still hitting secondly rate limits we lowered it to 4
*/
public function getPaginatedData(array $payload, string $type, int $offset = 0): array
{
$total = 0;
$lastId = null;
$rows = [];
foreach ($this->getPaginatedDataGenerator($payload, $type, $offset, $total, $lastId) as $row) {
$rows[] = $row;
}
return ['results' => $rows, 'total' => $total, 'last_record' => $lastId];
}
/**
* @throws HubspotException
* @throws SocialAccountTokenInvalidException
* @throws BadRequest
*/
public function getPaginatedDataGenerator(
array $payload,
string $type,
int $offset = 0,
int &$total = 0,
?string &$lastRecordId = null
): \Generator {
return $this->paginationService->getPaginatedDataGenerator(
$this,
$payload,
$type,
$offset,
$total,
$lastRecordId
);
}
/**
* Execute a search request against HubSpot CRM objects with rate limiting.
*
* @param string $objectType The object type ('deals', 'companies', 'contacts', 'calls')
* @param array<string, mixed> $payload The search payload with filters, sorts, properties, etc.
* @return array The search response with 'results', 'total', 'paging' keys
* @throws RateLimitException When rate limit is hit
* @throws HubspotException On API errors
*/
public function search(string $objectType, array $payload): array
{
$endpoint = self::BASE_URL . "/crm/v3/objects/{$objectType}/search";
return $this->executeRequest(function () use ($endpoint, $payload) {
$response = $this->getInstance()->getClient()->request('POST', $endpoint, ['json' => $payload]);
return $response->toArray();
});
}
/**
* @throws DealApiException
* @throws CrmException
*/
public function getOpportunityById(string $crmId, array $fields): array
{
try {
// $deal = $this->executeRequest(fn () => $this->getNewInstance()->crm()->deals()->basicApi()->getById(
$deal = $this->getNewInstance()->crm()->deals()->basicApi()->getById(
$crmId,
implode(',', $fields),
'companies,contacts'
);
} catch (DealApiException $e) {
$this->log->info('[Hubspot] Failed to fetch opportunity', [
'crm_id' => $crmId,
'reason' => $e->getMessage(),
]);
throw $e;
}
if (! $deal instanceof DealWithAssociations) {
throw new CrmException('Deal not found');
}
return [
'id' => $deal->getId(),
'properties' => $deal->getProperties(),
'associations' => $deal->getAssociations(),
];
}
/**
* Generic batch read method for HubSpot objects
*
* @param string $objectType The object type ('deals', 'companies', 'contacts')
* @param array<string> $crmIds Array of HubSpot object IDs (max 100)
* @param array<string> $fields Array of property names to fetch
*
* @return array<string, array> Array keyed by CRM ID with object data
*/
private function batchReadObjects(string $objectType, array $crmIds, array $fields): array
{
if (empty($crmIds)) {
return [];
}
$this->validateBatchSize($objectType, $crmIds);
$this->ensureValidToken();
try {
$batchConfig = $this->createBatchConfiguration($objectType);
$batchReadRequest = $this->prepareBatchRequest($batchConfig, $crmIds, $fields);
$response = $batchConfig['api']->read($batchReadRequest);
$this->validateApiResponse($response, $objectType);
$results = $this->processApiResults($response);
$this->logBatchResults($objectType, $crmIds, $results);
return $results;
} catch (\Throwable $e) {
$this->handleBatchError($e, $objectType, $crmIds);
}
}
private function validateBatchSize(string $objectType, array $crmIds): void
{
if (count($crmIds) > 100) {
throw new \InvalidArgumentException("Batch size cannot exceed 100 {$objectType}");
}
}
private function createBatchConfiguration(string $objectType): array
{
$configurations = [
'deals' => [
'batchReadRequest' => new \HubSpot\Client\Crm\Deals\Model\BatchReadInputSimplePublicObjectId(),
'inputClass' => \HubSpot\Client\Crm\Deals\Model\SimplePublicObjectId::class,
'api' => $this->getNewInstance()->crm()->deals()->batchApi(),
],
'companies' => [
'batchReadRequest' => new \HubSpot\Client\Crm\Companies\Model\BatchReadInputSimplePublicObjectId(),
'inputClass' => \HubSpot\Client\Crm\Companies\Model\SimplePublicObjectId::class,
'api' => $this->getNewInstance()->crm()->companies()->batchApi(),
],
'contacts' => [
'batchReadRequest' => new \HubSpot\Client\Crm\Contacts\Model\BatchReadInputSimplePublicObjectId(),
'inputClass' => \HubSpot\Client\Crm\Contacts\Model\SimplePublicObjectId::class,
'api' => $this->getNewInstance()->crm()->contacts()->batchApi(),
],
];
if (! isset($configurations[$objectType])) {
throw new \InvalidArgumentException("Unsupported object type: {$objectType}");
}
return $configurations[$objectType];
}
private function prepareBatchRequest(array $batchConfig, array $crmIds, array $fields): object
{
$batchReadRequest = $batchConfig['batchReadRequest'];
$inputClass = $batchConfig['inputClass'];
$inputs = array_map(function ($crmId) use ($inputClass) {
$input = new $inputClass();
$input->setId($crmId);
return $input;
}, $crmIds);
$batchReadRequest->setInputs($inputs);
$batchReadRequest->setProperties($fields);
return $batchReadRequest;
}
private function validateApiResponse($response, string $objectType): void
{
if (! $response) {
throw new CrmException("HubSpot API returned null response for {$objectType} batch read");
}
}
private function processApiResults($response): array
{
$results = [];
$responseResults = $response->getResults();
if ($responseResults) {
foreach ($responseResults as $object) {
if ($object && $object->getId()) {
$results[$object->getId()] = [
'id' => $object->getId(),
'properties' => $object->getProperties() ?: [],
];
}
}
}
return $results;
}
private function logBatchResults(string $objectType, array $crmIds, array $results): void
{
$this->log->info("[HubSpot] Batch fetched {$objectType}", [
'requested_count' => count($crmIds),
'returned_count' => count($results),
'crm_ids' => $crmIds,
]);
}
private function handleBatchError(\Throwable $e, string $objectType, array $crmIds): void
{
$errorMessage = $e->getMessage() ?: 'Unknown error';
$errorTrace = $e->getTraceAsString() ?: 'No trace available';
$this->log->error("[HubSpot] Failed to batch fetch {$objectType}", [
'crm_ids' => $crmIds,
'error' => $errorMessage,
'trace' => $errorTrace,
]);
throw new CrmException("Failed to batch fetch {$objectType}: " . $errorMessage);
}
/**
* Batch read multiple opportunities by their CRM IDs
*
* @param array<string> $crmIds Array of HubSpot deal IDs (max 100)
* @param array<string> $fields Array of property names to fetch
*
* @return array<string, array> Array keyed by CRM ID with opportunity data
*/
public function getOpportunitiesByIds(array $crmIds, array $fields): array
{
return $this->batchReadObjects('deals', $crmIds, $fields);
}
/**
* Batch read multiple companies by their CRM IDs
*
* @param array<string> $crmIds Array of HubSpot company IDs (max 100)
* @param array<string> $fields Array of property names to fetch
*
* @return array<string, array> Array keyed by CRM ID with company data
*/
public function getCompaniesByIds(array $crmIds, array $fields): array
{
return $this->batchReadObjects('companies', $crmIds, $fields);
}
/**
* Batch read multiple contacts by their CRM IDs
*
* @param array<string> $crmIds Array of HubSpot contact IDs (max 100)
* @param array<string> $fields Array of property names to fetch
*
* @return array<string, array> Array keyed by CRM ID with contact data
*/
public function getContactsByIds(array $crmIds, array $fields): array
{
return $this->batchReadObjects('contacts', $crmIds, $fields);
}
/**
* @throws CompanyApiException
* @throws CrmException
*/
public function getAccountById(string $crmId, array $fields): array
{
try {
$company = $this->getNewInstance()->crm()->companies()->basicApi()->getById(
$crmId,
implode(',', $fields),
);
} catch (CompanyApiException $e) {
$this->log->info('[Hubspot] Failed to fetch account', [
'crm_id' => $crmId,
'reason' => $e->getMessage(),
]);
throw $e;
}
if (! $company instanceof CompaniesWithAssociations) {
throw new CrmException('Account not found');
}
return [
'id' => $company->getId(),
'properties' => $company->getProperties(),
];
}
/**
* @throws ContactApiException
* @throws CrmException
*/
public function getContactById(string $crmId, array $fields): array
{
try {
$contact = $this->getNewInstance()->crm()->contacts()->basicApi()->getById(
$crmId,
implode(',', $fields)
);
} catch (ContactApiException $e) {
$this->log->info('[Hubspot] Failed to fetch contact', [
'crm_id' => $crmId,
'reason' => $e->getMessage(),
]);
throw $e;
}
if (! $contact instanceof ContactsWithAssociations) {
throw new CrmException('Contact not found');
}
return [
'id' => $contact->getId(),
'properties' => $contact->getProperties(),
];
}
/**
* This is email search request that Hubspot offers as GET (more generous quota)
*/
public function getContactByEmail(string $email, array $fields = []): array
{
try {
$contact = $this->getNewInstance()->crm()->contacts()->basicApi()->getById(
$email,
implode(',', $fields),
null,
false,
'email'
);
return [
'id' => $contact->getId(),
'properties' => $contact->getProperties(),
];
} catch (ContactApiException $e) {
$this->log->info('[Hubspot] Failed to fetch contact', [
'email' => $email,
'reason' => $e->getMessage(),
]);
return [];
}
}
/**
* @throws CrmException
*/
public function fetchProperty(string $objectType, string $propertyId): Property
{
$result = $this->getNewInstance()->crm()->properties()->coreApi()->getByName($objectType, $propertyId);
if (! $result instanceof Property) {
$this->log->error('[Hubspot] Failed to fetch property', [
'object_type' => $objectType,
'property_id' => $propertyId,
'reason' => $result->getMessage(),
]);
throw new CrmException('Failed to fetch property');
}
return $result;
}
/**
* @return array<CrmFieldOption>
*/
public function fetchPropertyOptions(string $objectType, string $propertyId): array
{
/** @var array<CrmFieldOption> */
return $this->fetchProperty($objectType, $propertyId)->getOptions();
}
/**
* @return array<array{id:string, label:string, deleted:bool}>
*/
public function fetchCallDispositions(): array
{
/** @var Response $response */
$response = $this->getInstance()->engagements()->getCallDispositions();
/**
* @var array<array{
* id:string,
* label:string,
* deleted: bool
* }>
*/
return $response->toArray();
}
/**
* @return array<CrmFieldOption>
*/
public function fetchOpportunityPipelineStages(): array
{
$stages = [];
$apiResponse = $this->getNewInstance()->crm()->pipelines()->pipelinesApi()->getAll('deals');
if ($apiResponse instanceof Error) {
$this->log->error('[Hubspot] Failed to fetch opportunity pipelines', [
'reason' => $apiResponse->getMessage(),
]);
return [];
}
foreach ($apiResponse->getResults() as $pipeline) {
$pipelineStages = array_map(
static function (PipelineStage $stage) {
return [
'id' => $stage->getId(),
'label' => $stage->getLabel(),
];
},
$pipeline->getStages()
);
$stages = array_merge($stages, $pipelineStages);
}
return $stages;
}
public function fetchOpportunityPipelines(): array
{
$pipelines = [];
try {
$apiResponse = $this->makeRequest('/crm/v3/pipelines/deals');
} catch (\Exception $e) {
$this->log->info('[Hubspot] Failed to fetch opportunity pipelines', [
'reason' => $e->getMessage(),
]);
return [];
}
$response = $apiResponse->toArray();
foreach ($response['results'] as $pipeline) {
$pipelines[] = [
'id' => $pipeline['id'],
'label' => $pipeline['label'],
];
}
return $pipelines;
}
/**
* @return array<CrmFieldOption>
*/
public function fetchMeetingOutcomeFieldOptions(Field $field): array
{
return $field->getCrmProviderId() === 'meetingOutcome'
? $this->fetchMeetingOutcomeTypes()
: $this->fetchCallActivityTypes();
}
public function fetchMeetingOutcomeTypes(): array
{
return $this->extractMeetingTypeOptions(
'[URL_WITH_CREDENTIALS] Response $response */
$response = $this->getInstance()
->getClient()
->request('GET', $endpoint);
/**
* @var array<array{
* value: string,
* label: string,
* displayOrder: int
* }> $optionData
*/
$optionData = $response->toArray()['options'] ?? [];
$options = [];
foreach ($optionData as $item) {
$options[] = [
'id' => $item['value'],
'value' => $item['value'],
'label' => $item['label'],
'display_order' => $item['displayOrder'],
];
}
return $options;
}
/**
* @return array<CrmFieldOption>
*/
public function fetchDispositionFieldOptions(): array
{
$options = [];
$dispositions = $this->fetchCallDispositions();
foreach ($dispositions as $disposition) {
if ($disposition['deleted'] !== false) {
continue;
}
$option['value'] = $disposition['id'];
$option['id'] = $disposition['id'];
$option['label'] = $disposition['label'];
$options[] = $option;
}
return $options;
}
/**
* @return array<CrmFieldOption>
*/
public function fetchOpportunityFieldOptions(Field $field): array
{
if ($field->isStageField()) {
return $this->fetchOpportunityPipelineStages();
}
if ($field->isPipelineField()) {
return $this->fetchOpportunityPipelines();
}
return $this->fetchPropertyOptions('deals', $field->getCrmProviderId());
}
/**
* @throws BadRequest
* @throws HubspotException
*/
public function makeRequest(string $endpoint, $method = 'GET', $payload = [], ?string $queryString = null)
{
$endpoint = self::BASE_URL . $endpoint;
if ($method === 'GET') {
return $this->getInstance()->getClient()?->request(
method: $method,
endpoint: $endpoint,
query_string: $queryString
);
} else {
return $this->getInstance()->getClient()->request($method, $endpoint, [
'json' => ($payload),
]);
}
}
/**
* @throws BadRequest
* @throws HubspotException
*/
public function createMeeting(array $payload): Response
{
$endpoint = '/crm/v3/objects/meetings';
return $this->makeRequest($endpoint, 'POST', $payload);
}
/**
* @throws BadRequest
* @throws HubspotException
*/
public function updateMeeting(string $meetingId, array $payload): Response
{
$endpoint = '/crm/v3/objects/meetings/' . $meetingId;
return $this->makeRequest($endpoint, 'PATCH', $payload);
}
/**
* @throws \Exception
*/
public function createNote(
string $body,
string $ownerId,
int $timestamp,
string $objectId,
NoteObject $noteObject
): ?string {
try {
$noteInput = new SimplePublicObjectInput([
'properties' => [
'hs_note_body' => $body,
'hubspot_owner_id' => $ownerId,
'hs_timestamp' => $timestamp,
],
]);
// Create note
$note = $this->getNewInstance()->crm()->objects()->basicApi()->create('note', $noteInput);
$this->getNewInstance()->crm()->objects()->associationsApi()->create(
'note',
$note->getId(),
$this->getNoteObject($noteObject),
$objectId,
$this->getNoteAssociationType($noteObject),
);
return $note->getId();
} catch (\Exception $e) {
$this->log->error('[Hubspot] Failed to create note', [
'objectId' => $objectId,
'noteObject' => $noteObject->getObjectType(),
'reason' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
return null;
}
public function updateEngagement(string $objectId, array $engagement, array $metadata): void
{
$this->getInstance()->engagements()->update($objectId, $engagement, $metadata);
}
public function getEngagementData(string $engagementId): array
{
$engagement = $this->getInstance()->engagements()->get($engagementId);
return $engagement->toArray();
}
public function createEngagement(array $engagement, array $associations, array $metadata): Response
{
return $this->getInstance()
->engagements()
->create($engagement, $associations, $metadata);
}
public function isUnauthorizedException(\Exception $e): bool
{
// Check for specific HubSpot API exception types first
if ($e instanceof BadRequest) {
// BadRequest can contain 401 status codes
return $e->getCode() === 401;
}
// Check for HTTP client exceptions with status codes
if ($e instanceof \GuzzleHttp\Exception\RequestException && $e->hasResponse()) {
$response = $e->getResponse();
if ($response !== null) {
return $response->getStatusCode() === 401;
}
}
// Check for Guzzle HTTP exceptions
if ($e instanceof \GuzzleHttp\Exception\ClientException) {
return $e->getCode() === 401;
}
// Fallback to string matching as last resort, but be more specific
$message = strtolower($e->getMessage());
return str_contains($message, '401 unauthorized') ||
str_contains($message, 'http 401') ||
str_contains($message, 'status code 401') ||
(preg_match('/\b401\b/', $message) && str_contains($message, 'unauthorized'));
}
/**
* Validates and refreshes the access token if needed before API requests.
* This ensures long-running processes don't fail due to token expiration.
*
* @throws SocialAccountTokenInvalidException
*/
public function ensureValidToken(): void
{
if ($this->oauthAccount === null) {
return;
}
$newToken = $this->tokenManager->ensureValidToken($this->oauthAccount);
if ($newToken !== null) {
$this->accessToken = $newToken;
}
}
public function getConfig()
{
return $this->config;
}
// returns only active (archived=false)
public function getOwners(): array
{
return $this->getNewInstance()->crm()->owners()->getAll();
}
/**
* @param bool $archived
*
* @return array<Owner>|[]
*/
public function getOwnersArchived(bool $archived = true): array
{
$endpoint = '/crm/v3/owners';
$queryParams = [
'archived' => $archived ? 'true' : 'false',
];
$queryString = http_build_query($queryParams);
$owners = [];
try {
$response = $this->makeRequest(endpoint: $endpoint, queryString: $queryString);
$responseData = $response?->toArray();
foreach ($responseData['results'] as $result) {
try {
$owners[] = Owner::create($result);
} catch (Throwable $e) {
$this->log->error('[HubSpot] Failed to process owner data', [
'result' => $result,
'error' => $e->getMessage(),
]);
continue;
}
}
} catch (Throwable $e) {
$this->log->error('HubSpot] Failed to fetch owners', [
'archived' => $archived,
'error' => $e->getMessage(),
]);
return [];
}
return $owners;
}
public function getMeeting(string $engagementId): ObjectWithAssociations
{
return $this->getNewInstance()->crm()->objects()->basicApi()
->getById('meeting', $engagementId, null, 'contact,company,deal');
}
public function deleteEngagement(string $engagementId): void
{
$this->getInstance()->engagements()->delete((int) $engagementId);
}
public function getAssociationsData(array $ids, string $fromObject, string $toObject): array
{
$associationData = [];
$idChunks = array_chunk($ids, self::ASSOCIATIONS_BATCH_SIZE_LIMIT);
foreach ($idChunks as $idChunk) {
try {
$batchInput = new \HubSpot\Client\Crm\Associations\Model\BatchInputPublicObjectId();
$batchInput->setInputs(array_map(function ($id) {
$publicObjectId = new \HubSpot\Client\Crm\Associations\Model\PublicObjectId();
$publicObjectId->setId($id);
return $publicObjectId;
}, $idChunk));
$associatedObjectsData = $this
->getNewInstance()
->crm()
->associations()
->batchApi()
->read($fromObject, $toObject, $batchInput);
if ($associatedObjectsData instanceof \HubSpot\Client\Crm\Associations\Model\BatchResponsePublicAssociationMulti) {
foreach ($associatedObjectsData->getResults() as $association) {
$from = $association->getFrom()->getId();
$toAssociations = $association->getTo();
if (! empty($toAssociations)) {
$associationData[$from] = array_map(function ($item) {
return $item->getId();
}, $toAssociations);
}
}
}
// } catch (RateLimitException $e) {
// throw $e;
} catch (\Exception $e) {
$this->log->error('[Hubspot] Failed to fetch associations', [
'from_object' => $fromObject,
'to_object' => $toObject,
'reason' => $e->getMessage(),
]);
}
}
return $associationData;
}
/**
* @throws \Exception
*/
private function getNoteAssociationType(NoteObject $noteObject): string
{
return match($noteObject) {
NoteObject::Opportunity => 'note_to_deal',
NoteObject::Lead, NoteObject::Contact => 'note_to_contact', // or 'note_to_lead' if your portal supports it
NoteObject::Account => 'note_to_company',
NoteObject::Call, NoteObject::Event => throw new \Exception('Not supported'),
};
}
/**
* @throws \Exception
*/
private function getNoteObject(NoteObject $noteObject): string
{
return match($noteObject) {
NoteObject::Opportunity => 'deal',
NoteObject::Lead, NoteObject::Contact => 'contact',
NoteObject::Account => 'company',
NoteObject::Call, NoteObject::Event => throw new \Exception('Not supported'),
};
}
public function addAssociations(string $objectType, string $associationType, array $payload): Response
{
$endpoint = "/crm/v4/associations/$objectType/$associationType/batch/create";
return $this->makeRequest($endpoint, 'POST', $payload);
}
public function removeAssociations(string $objectType, string $associationType, array $payload): Response
{
$endpoint = "/crm/v4/associations/$objectType/$associationType/batch/archive";
return $this->makeRequest($endpoint, 'POST', $payload);
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
19
Previous Highlighted Error
Next Highlighted Error
[2026-05-07 14:21:15] local.INFO: [Hubspot] DEBUG Getting headers {
"headers":{
"Date":["Thu,07 May 2026 14:21:15 GMT"],
"Content-Type":["application/json;charset=utf-8"],
"Transfer-Encoding":["chunked"],
"Connection":["keep-alive"],
"CF-Ray":["9f80deb8db60dc3a-SOF"],
"CF-Cache-Status":["DYNAMIC"],
"Strict-Transport-Security":["max-age=31536000; includeSubDomains; preload"],
"Vary":["origin,
accept-encoding"],
"access-control-allow-credentials":["false"],
"server-timing":["hcid;desc=\"019e02d0-6fd8-7812-bdba-885b7ccb3ee3\",
cfr;desc=\"9f80deb8e7c6dc3a-IAD\""],
"x-content-type-options":["nosniff"],
"x-hubspot-correlation-id":["019e02d0-6fd8-7812-bdba-885b7ccb3ee3"],
"Set-Cookie":["__cf_bm=SIUrtdQgXVrik50pdqF6hZVYKhzTnQBidvMabeCtm0Y-1778163675-[IP_ADDRESS]-rI.ZggtDKxTge5zr8_2gbBfWMQQ.ufZEXDZyHz2mBUFdzdo2gTHEsOkXMSEShjK0hGYxNhUGM1ZoBpX7BcFZcHEjA7Cs_.SMUhUnd2nYjko; path=/; expires=Thu,
07-May-26 14:51:15 GMT; domain=.hubapi.com; HttpOnly; Secure; SameSite=None"],
"Report-To":["{
\"endpoints\":[{
\"url\":\"https:\\/\\/a.nel.cloudflare.com\\/report\\/v4?s=NYAlsVTP0fYm32qrSDjxYE4sd2RWRqiSp3wHsmdEgZlzoYdxI%2BIxVpHmsKn3O%2BKVA3mFIJ2m7YRECDGSM%2BW2IYTzo6FM4%2BdUIjURO8srzKSvJgZ%2BQ6R79arKQw3uHLlX\"}],
\"group\":\"cf-nel\",
\"max_age\":604800}"],
"NEL":["{
\"success_fraction\":0.01,
\"report_to\":\"cf-nel\",
\"max_age\":604800}"],
"Server":["cloudflare"]}} {
"correlation_id":"95236535-ec98-4541-b92a-adfa73b69eab",
"trace_id":"c7ab8365-903f-46d4-9403-0e5b551e3545"}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JY-20725-handle-HS-search-rate-limit, menu","depth":5,"on_screen":true,"help_text":"Git Branch: JY-20725-handle-HS-search-rate-limit","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"HandleHubspotRateLimitTest","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'HandleHubspotRateLimitTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'HandleHubspotRateLimitTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"2","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"67","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"3","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Services\\Crm\\Hubspot;\n\nuse HubSpot\\Client\\Crm\\Deals\\ApiException as DealApiException;\nuse HubSpot\\Client\\Crm\\Contacts\\ApiException as ContactApiException;\nuse HubSpot\\Client\\Crm\\Companies\\ApiException as CompanyApiException;\nuse HubSpot\\Client\\Crm\\Contacts\\Model\\SimplePublicObjectWithAssociations as ContactsWithAssociations;\nuse HubSpot\\Client\\Crm\\Companies\\Model\\SimplePublicObjectWithAssociations as CompaniesWithAssociations;\nuse HubSpot\\Client\\Crm\\Deals\\Model\\SimplePublicObjectWithAssociations as DealWithAssociations;\nuse HubSpot\\Client\\Crm\\Objects\\Model\\SimplePublicObjectInput;\nuse HubSpot\\Client\\Crm\\Objects\\Model\\SimplePublicObjectWithAssociations as ObjectWithAssociations;\nuse HubSpot\\Client\\Crm\\Pipelines\\Model\\Error;\nuse HubSpot\\Client\\Crm\\Pipelines\\Model\\PipelineStage;\nuse HubSpot\\Client\\Crm\\Properties\\Model\\Property;\nuse HubSpot\\Discovery\\Discovery;\nuse Jiminny\\Exceptions\\CrmException;\nuse Jiminny\\Exceptions\\RateLimitException;\nuse Jiminny\\Exceptions\\SocialAccountTokenInvalidException;\nuse Jiminny\\Jobs\\Crm\\NoteObject;\nuse Jiminny\\Models\\Crm\\Field;\nuse Jiminny\\Services\\Crm\\BaseClient;\nuse Jiminny\\Services\\Crm\\Hubspot\\DTO\\Response\\Owner;\nuse Jiminny\\Services\\SocialAccountService;\nuse SevenShores\\Hubspot\\Exceptions\\BadRequest;\nuse SevenShores\\Hubspot\\Exceptions\\HubspotException;\nuse SevenShores\\Hubspot\\Factory;\nuse SevenShores\\Hubspot\\Http\\Response;\nuse Jiminny\\Services\\Crm\\Hubspot\\Pagination\\HubspotPaginationService;\nuse Illuminate\\Support\\Facades\\Redis;\nuse Throwable;\n\n/**\n * @phpstan-type CrmFieldOption array{id:string, label:string, value?:string}\n */\nclass Client extends BaseClient implements HubspotClientInterface\n{\n public const string MIN_API_VERSION = '2';\n\n public const string BASE_URL = 'https://api.hubapi.com';\n\n public const int ASSOCIATIONS_BATCH_SIZE_LIMIT = 1000;\n\n private HubspotPaginationService $paginationService;\n private HubspotTokenManager $tokenManager;\n\n public function __construct(\n SocialAccountService $socialAccountService,\n HubspotPaginationService $paginationService,\n HubspotTokenManager $tokenManager\n ) {\n parent::__construct($socialAccountService);\n $this->paginationService = $paginationService;\n $this->tokenManager = $tokenManager;\n\n $this->setBaseUrl(self::BASE_URL);\n $this->setVersion(self::MIN_API_VERSION);\n }\n\n /**\n * Reacts to a rate limits (429) from HubSpot by translating it\n * into a RateLimitException carrying retry_after.\n *\n * Wrap any outbound HubSpot call (SDK or raw HTTP) like:\n *\n * $this->executeRequest(fn () => $this->getNewInstance()->crm()->...);\n *\n * @template T\n * @param callable(): T $apiCall\n * @return T\n *\n * @throws RateLimitException\n */\n private function executeRequest(callable $apiCall)\n {\n $cacheKey = $this->getRateLimitCacheKey();\n\n $cachedRetryAfter = Redis::get($cacheKey);\n if (is_string($cachedRetryAfter) && is_numeric($cachedRetryAfter)) {\n throw new RateLimitException(\n 'Hubspot rate limit (cached circuit-breaker)',\n (int) $cachedRetryAfter,\n );\n }\n\n try {\n return $apiCall();\n } catch (Throwable $e) {\n if ($this->isHubspotRateLimit($e)) {\n $retryAfter = $this->parseRetryAfter($e);\n\n Redis::setex($cacheKey, $retryAfter, (string) $retryAfter);\n\n $this->log->warning('[Hubspot] Received 429 from API', [\n 'team_id' => $this->config->team_id,\n 'config_id' => $this->config->getId(),\n 'retry_after' => $retryAfter,\n 'message' => $this->parseMessage($e),\n 'reason' => $e->getMessage(),\n ]);\n\n throw new RateLimitException('Hubspot returned 429', $retryAfter, $e);\n }\n\n throw $e;\n }\n }\n\n private function getRateLimitCacheKey(): string\n {\n return sprintf('hubspot:ratelimit:portal:%d', $this->config->getId());\n }\n\n public function isHubspotRateLimit(Throwable $e): bool\n {\n if ($e instanceof BadRequest\n || $e instanceof DealApiException\n || $e instanceof ContactApiException\n || $e instanceof CompanyApiException\n || $e instanceof \\GuzzleHttp\\Exception\\RequestException\n ) {\n return (int) $e->getCode() === 429;\n }\n\n return false;\n }\n\n public function parseRetryAfter(Throwable $e): int\n {\n if (method_exists($e, 'getResponseHeaders')) {\n $headers = $e->getResponseHeaders() ?: [];\n $value = $headers['Retry-After'] ?? $headers['retry-after'] ?? null;\n if (is_array($value)) {\n $value = $value[0] ?? null;\n }\n if (is_numeric($value)) {\n return (int) $value;\n }\n }\n\n $message = strtolower($this->parseMessage($e));\n\n if (str_contains($message, 'daily')) {\n return 600;\n }\n if (str_contains($message, 'ten secondly')) {\n return 10;\n }\n if (str_contains($message, 'secondly')) {\n return 1;\n }\n\n $this->log->warning('[Hubspot] No retry-after header or known message, using default', [\n 'exception_class' => get_class($e),\n 'message' => $message,\n ]);\n\n return 10;\n }\n\n public function parseMessage(Throwable $e): string\n {\n if (! method_exists($e, 'getResponseBody')) {\n return '';\n }\n\n $body = $e->getResponseBody();\n if (is_string($body)) {\n $body = json_decode($body, true) ?? [];\n }\n\n if (! is_array($body)) {\n return '';\n }\n\n return (string) ($body['message'] ?? '');\n }\n\n public function getMinimumApiVersion(): string\n {\n return self::MIN_API_VERSION;\n }\n\n public function getInstance(): Factory\n {\n return new Factory([\n 'key' => $this->accessToken,\n 'oauth2' => true,\n 'base_url' => $this->baseUrl,\n ]);\n }\n\n public function getNewInstance(): Discovery\n {\n return \\HubSpot\\Factory::createWithAccessToken($this->accessToken);\n }\n\n /**\n * Secondly and daily limits for Hubspot API\n *\n * Product Tier: Free & Starter | Professional & Enterprise | API add-on (any tier)\n * Burst: 100/10 seconds | 150/10 seconds | 200/10 seconds\n * Daily: 250,000 | 500,000 | 1,000,000\n *\n * Official documentation states: The search endpoints are rate limited to five requests per second.\n * Since with 5 RPS were still hitting secondly rate limits we lowered it to 4\n */\n public function getPaginatedData(array $payload, string $type, int $offset = 0): array\n {\n $total = 0;\n $lastId = null;\n $rows = [];\n foreach ($this->getPaginatedDataGenerator($payload, $type, $offset, $total, $lastId) as $row) {\n $rows[] = $row;\n }\n\n return ['results' => $rows, 'total' => $total, 'last_record' => $lastId];\n }\n\n /**\n * @throws HubspotException\n * @throws SocialAccountTokenInvalidException\n * @throws BadRequest\n */\n public function getPaginatedDataGenerator(\n array $payload,\n string $type,\n int $offset = 0,\n int &$total = 0,\n ?string &$lastRecordId = null\n ): \\Generator {\n return $this->paginationService->getPaginatedDataGenerator(\n $this,\n $payload,\n $type,\n $offset,\n $total,\n $lastRecordId\n );\n }\n\n /**\n * Execute a search request against HubSpot CRM objects with rate limiting.\n *\n * @param string $objectType The object type ('deals', 'companies', 'contacts', 'calls')\n * @param array<string, mixed> $payload The search payload with filters, sorts, properties, etc.\n * @return array The search response with 'results', 'total', 'paging' keys\n * @throws RateLimitException When rate limit is hit\n * @throws HubspotException On API errors\n */\n public function search(string $objectType, array $payload): array\n {\n $endpoint = self::BASE_URL . \"/crm/v3/objects/{$objectType}/search\";\n\n return $this->executeRequest(function () use ($endpoint, $payload) {\n $response = $this->getInstance()->getClient()->request('POST', $endpoint, ['json' => $payload]);\n\n return $response->toArray();\n });\n }\n\n /**\n * @throws DealApiException\n * @throws CrmException\n */\n public function getOpportunityById(string $crmId, array $fields): array\n {\n try {\n// $deal = $this->executeRequest(fn () => $this->getNewInstance()->crm()->deals()->basicApi()->getById(\n $deal = $this->getNewInstance()->crm()->deals()->basicApi()->getById(\n $crmId,\n implode(',', $fields),\n 'companies,contacts'\n );\n } catch (DealApiException $e) {\n $this->log->info('[Hubspot] Failed to fetch opportunity', [\n 'crm_id' => $crmId,\n 'reason' => $e->getMessage(),\n ]);\n\n throw $e;\n }\n\n if (! $deal instanceof DealWithAssociations) {\n throw new CrmException('Deal not found');\n }\n\n return [\n 'id' => $deal->getId(),\n 'properties' => $deal->getProperties(),\n 'associations' => $deal->getAssociations(),\n ];\n }\n\n /**\n * Generic batch read method for HubSpot objects\n *\n * @param string $objectType The object type ('deals', 'companies', 'contacts')\n * @param array<string> $crmIds Array of HubSpot object IDs (max 100)\n * @param array<string> $fields Array of property names to fetch\n *\n * @return array<string, array> Array keyed by CRM ID with object data\n */\n private function batchReadObjects(string $objectType, array $crmIds, array $fields): array\n {\n if (empty($crmIds)) {\n return [];\n }\n\n $this->validateBatchSize($objectType, $crmIds);\n $this->ensureValidToken();\n\n try {\n $batchConfig = $this->createBatchConfiguration($objectType);\n $batchReadRequest = $this->prepareBatchRequest($batchConfig, $crmIds, $fields);\n $response = $batchConfig['api']->read($batchReadRequest);\n\n $this->validateApiResponse($response, $objectType);\n\n $results = $this->processApiResults($response);\n $this->logBatchResults($objectType, $crmIds, $results);\n\n return $results;\n } catch (\\Throwable $e) {\n $this->handleBatchError($e, $objectType, $crmIds);\n }\n }\n\n private function validateBatchSize(string $objectType, array $crmIds): void\n {\n if (count($crmIds) > 100) {\n throw new \\InvalidArgumentException(\"Batch size cannot exceed 100 {$objectType}\");\n }\n }\n\n private function createBatchConfiguration(string $objectType): array\n {\n $configurations = [\n 'deals' => [\n 'batchReadRequest' => new \\HubSpot\\Client\\Crm\\Deals\\Model\\BatchReadInputSimplePublicObjectId(),\n 'inputClass' => \\HubSpot\\Client\\Crm\\Deals\\Model\\SimplePublicObjectId::class,\n 'api' => $this->getNewInstance()->crm()->deals()->batchApi(),\n ],\n 'companies' => [\n 'batchReadRequest' => new \\HubSpot\\Client\\Crm\\Companies\\Model\\BatchReadInputSimplePublicObjectId(),\n 'inputClass' => \\HubSpot\\Client\\Crm\\Companies\\Model\\SimplePublicObjectId::class,\n 'api' => $this->getNewInstance()->crm()->companies()->batchApi(),\n ],\n 'contacts' => [\n 'batchReadRequest' => new \\HubSpot\\Client\\Crm\\Contacts\\Model\\BatchReadInputSimplePublicObjectId(),\n 'inputClass' => \\HubSpot\\Client\\Crm\\Contacts\\Model\\SimplePublicObjectId::class,\n 'api' => $this->getNewInstance()->crm()->contacts()->batchApi(),\n ],\n ];\n\n if (! isset($configurations[$objectType])) {\n throw new \\InvalidArgumentException(\"Unsupported object type: {$objectType}\");\n }\n\n return $configurations[$objectType];\n }\n\n private function prepareBatchRequest(array $batchConfig, array $crmIds, array $fields): object\n {\n $batchReadRequest = $batchConfig['batchReadRequest'];\n $inputClass = $batchConfig['inputClass'];\n\n $inputs = array_map(function ($crmId) use ($inputClass) {\n $input = new $inputClass();\n $input->setId($crmId);\n\n return $input;\n }, $crmIds);\n\n $batchReadRequest->setInputs($inputs);\n $batchReadRequest->setProperties($fields);\n\n return $batchReadRequest;\n }\n\n private function validateApiResponse($response, string $objectType): void\n {\n if (! $response) {\n throw new CrmException(\"HubSpot API returned null response for {$objectType} batch read\");\n }\n }\n\n private function processApiResults($response): array\n {\n $results = [];\n $responseResults = $response->getResults();\n\n if ($responseResults) {\n foreach ($responseResults as $object) {\n if ($object && $object->getId()) {\n $results[$object->getId()] = [\n 'id' => $object->getId(),\n 'properties' => $object->getProperties() ?: [],\n ];\n }\n }\n }\n\n return $results;\n }\n\n private function logBatchResults(string $objectType, array $crmIds, array $results): void\n {\n $this->log->info(\"[HubSpot] Batch fetched {$objectType}\", [\n 'requested_count' => count($crmIds),\n 'returned_count' => count($results),\n 'crm_ids' => $crmIds,\n ]);\n }\n\n private function handleBatchError(\\Throwable $e, string $objectType, array $crmIds): void\n {\n $errorMessage = $e->getMessage() ?: 'Unknown error';\n $errorTrace = $e->getTraceAsString() ?: 'No trace available';\n\n $this->log->error(\"[HubSpot] Failed to batch fetch {$objectType}\", [\n 'crm_ids' => $crmIds,\n 'error' => $errorMessage,\n 'trace' => $errorTrace,\n ]);\n\n throw new CrmException(\"Failed to batch fetch {$objectType}: \" . $errorMessage);\n }\n\n /**\n * Batch read multiple opportunities by their CRM IDs\n *\n * @param array<string> $crmIds Array of HubSpot deal IDs (max 100)\n * @param array<string> $fields Array of property names to fetch\n *\n * @return array<string, array> Array keyed by CRM ID with opportunity data\n */\n public function getOpportunitiesByIds(array $crmIds, array $fields): array\n {\n return $this->batchReadObjects('deals', $crmIds, $fields);\n }\n\n /**\n * Batch read multiple companies by their CRM IDs\n *\n * @param array<string> $crmIds Array of HubSpot company IDs (max 100)\n * @param array<string> $fields Array of property names to fetch\n *\n * @return array<string, array> Array keyed by CRM ID with company data\n */\n public function getCompaniesByIds(array $crmIds, array $fields): array\n {\n return $this->batchReadObjects('companies', $crmIds, $fields);\n }\n\n /**\n * Batch read multiple contacts by their CRM IDs\n *\n * @param array<string> $crmIds Array of HubSpot contact IDs (max 100)\n * @param array<string> $fields Array of property names to fetch\n *\n * @return array<string, array> Array keyed by CRM ID with contact data\n */\n public function getContactsByIds(array $crmIds, array $fields): array\n {\n return $this->batchReadObjects('contacts', $crmIds, $fields);\n }\n\n /**\n * @throws CompanyApiException\n * @throws CrmException\n */\n public function getAccountById(string $crmId, array $fields): array\n {\n try {\n $company = $this->getNewInstance()->crm()->companies()->basicApi()->getById(\n $crmId,\n implode(',', $fields),\n );\n } catch (CompanyApiException $e) {\n $this->log->info('[Hubspot] Failed to fetch account', [\n 'crm_id' => $crmId,\n 'reason' => $e->getMessage(),\n ]);\n\n throw $e;\n }\n\n if (! $company instanceof CompaniesWithAssociations) {\n throw new CrmException('Account not found');\n }\n\n return [\n 'id' => $company->getId(),\n 'properties' => $company->getProperties(),\n ];\n }\n\n /**\n * @throws ContactApiException\n * @throws CrmException\n */\n public function getContactById(string $crmId, array $fields): array\n {\n try {\n $contact = $this->getNewInstance()->crm()->contacts()->basicApi()->getById(\n $crmId,\n implode(',', $fields)\n );\n } catch (ContactApiException $e) {\n $this->log->info('[Hubspot] Failed to fetch contact', [\n 'crm_id' => $crmId,\n 'reason' => $e->getMessage(),\n ]);\n\n throw $e;\n }\n\n if (! $contact instanceof ContactsWithAssociations) {\n throw new CrmException('Contact not found');\n }\n\n return [\n 'id' => $contact->getId(),\n 'properties' => $contact->getProperties(),\n ];\n }\n\n /**\n * This is email search request that Hubspot offers as GET (more generous quota)\n */\n public function getContactByEmail(string $email, array $fields = []): array\n {\n try {\n $contact = $this->getNewInstance()->crm()->contacts()->basicApi()->getById(\n $email,\n implode(',', $fields),\n null,\n false,\n 'email'\n );\n\n return [\n 'id' => $contact->getId(),\n 'properties' => $contact->getProperties(),\n ];\n } catch (ContactApiException $e) {\n $this->log->info('[Hubspot] Failed to fetch contact', [\n 'email' => $email,\n 'reason' => $e->getMessage(),\n ]);\n\n return [];\n }\n }\n\n /**\n * @throws CrmException\n */\n public function fetchProperty(string $objectType, string $propertyId): Property\n {\n $result = $this->getNewInstance()->crm()->properties()->coreApi()->getByName($objectType, $propertyId);\n\n if (! $result instanceof Property) {\n $this->log->error('[Hubspot] Failed to fetch property', [\n 'object_type' => $objectType,\n 'property_id' => $propertyId,\n 'reason' => $result->getMessage(),\n ]);\n\n throw new CrmException('Failed to fetch property');\n }\n\n return $result;\n }\n\n /**\n * @return array<CrmFieldOption>\n */\n public function fetchPropertyOptions(string $objectType, string $propertyId): array\n {\n /** @var array<CrmFieldOption> */\n return $this->fetchProperty($objectType, $propertyId)->getOptions();\n }\n\n /**\n * @return array<array{id:string, label:string, deleted:bool}>\n */\n public function fetchCallDispositions(): array\n {\n /** @var Response $response */\n $response = $this->getInstance()->engagements()->getCallDispositions();\n\n /**\n * @var array<array{\n * id:string,\n * label:string,\n * deleted: bool\n * }>\n */\n return $response->toArray();\n }\n\n /**\n * @return array<CrmFieldOption>\n */\n public function fetchOpportunityPipelineStages(): array\n {\n $stages = [];\n $apiResponse = $this->getNewInstance()->crm()->pipelines()->pipelinesApi()->getAll('deals');\n\n if ($apiResponse instanceof Error) {\n $this->log->error('[Hubspot] Failed to fetch opportunity pipelines', [\n 'reason' => $apiResponse->getMessage(),\n ]);\n\n return [];\n }\n\n foreach ($apiResponse->getResults() as $pipeline) {\n $pipelineStages = array_map(\n static function (PipelineStage $stage) {\n return [\n 'id' => $stage->getId(),\n 'label' => $stage->getLabel(),\n ];\n },\n $pipeline->getStages()\n );\n\n $stages = array_merge($stages, $pipelineStages);\n }\n\n return $stages;\n }\n\n public function fetchOpportunityPipelines(): array\n {\n $pipelines = [];\n\n try {\n $apiResponse = $this->makeRequest('/crm/v3/pipelines/deals');\n } catch (\\Exception $e) {\n $this->log->info('[Hubspot] Failed to fetch opportunity pipelines', [\n 'reason' => $e->getMessage(),\n ]);\n\n return [];\n }\n\n $response = $apiResponse->toArray();\n\n foreach ($response['results'] as $pipeline) {\n $pipelines[] = [\n 'id' => $pipeline['id'],\n 'label' => $pipeline['label'],\n ];\n }\n\n return $pipelines;\n }\n\n /**\n * @return array<CrmFieldOption>\n */\n public function fetchMeetingOutcomeFieldOptions(Field $field): array\n {\n return $field->getCrmProviderId() === 'meetingOutcome'\n ? $this->fetchMeetingOutcomeTypes()\n : $this->fetchCallActivityTypes();\n }\n\n public function fetchMeetingOutcomeTypes(): array\n {\n return $this->extractMeetingTypeOptions(\n 'https://api.hubapi.com/crm/v3/properties/meeting/hs_meeting_outcome'\n );\n }\n\n public function fetchCallActivityTypes(): array\n {\n return $this->extractMeetingTypeOptions(\n 'https://api.hubapi.com/crm/v3/properties/call/hs_activity_type'\n );\n }\n\n private function extractMeetingTypeOptions(string $endpoint): array\n {\n /** @var Response $response */\n $response = $this->getInstance()\n ->getClient()\n ->request('GET', $endpoint);\n\n /**\n * @var array<array{\n * value: string,\n * label: string,\n * displayOrder: int\n * }> $optionData\n */\n $optionData = $response->toArray()['options'] ?? [];\n\n $options = [];\n foreach ($optionData as $item) {\n $options[] = [\n 'id' => $item['value'],\n 'value' => $item['value'],\n 'label' => $item['label'],\n 'display_order' => $item['displayOrder'],\n ];\n }\n\n return $options;\n }\n\n /**\n * @return array<CrmFieldOption>\n */\n public function fetchDispositionFieldOptions(): array\n {\n $options = [];\n\n $dispositions = $this->fetchCallDispositions();\n\n foreach ($dispositions as $disposition) {\n if ($disposition['deleted'] !== false) {\n continue;\n }\n\n $option['value'] = $disposition['id'];\n $option['id'] = $disposition['id'];\n $option['label'] = $disposition['label'];\n\n $options[] = $option;\n }\n\n return $options;\n }\n\n /**\n * @return array<CrmFieldOption>\n */\n public function fetchOpportunityFieldOptions(Field $field): array\n {\n if ($field->isStageField()) {\n return $this->fetchOpportunityPipelineStages();\n }\n\n if ($field->isPipelineField()) {\n return $this->fetchOpportunityPipelines();\n }\n\n return $this->fetchPropertyOptions('deals', $field->getCrmProviderId());\n }\n\n /**\n * @throws BadRequest\n * @throws HubspotException\n */\n public function makeRequest(string $endpoint, $method = 'GET', $payload = [], ?string $queryString = null)\n {\n $endpoint = self::BASE_URL . $endpoint;\n\n if ($method === 'GET') {\n return $this->getInstance()->getClient()?->request(\n method: $method,\n endpoint: $endpoint,\n query_string: $queryString\n );\n } else {\n return $this->getInstance()->getClient()->request($method, $endpoint, [\n 'json' => ($payload),\n ]);\n }\n }\n\n /**\n * @throws BadRequest\n * @throws HubspotException\n */\n public function createMeeting(array $payload): Response\n {\n $endpoint = '/crm/v3/objects/meetings';\n\n return $this->makeRequest($endpoint, 'POST', $payload);\n }\n\n /**\n * @throws BadRequest\n * @throws HubspotException\n */\n public function updateMeeting(string $meetingId, array $payload): Response\n {\n $endpoint = '/crm/v3/objects/meetings/' . $meetingId;\n\n return $this->makeRequest($endpoint, 'PATCH', $payload);\n }\n\n /**\n * @throws \\Exception\n */\n public function createNote(\n string $body,\n string $ownerId,\n int $timestamp,\n string $objectId,\n NoteObject $noteObject\n ): ?string {\n try {\n $noteInput = new SimplePublicObjectInput([\n 'properties' => [\n 'hs_note_body' => $body,\n 'hubspot_owner_id' => $ownerId,\n 'hs_timestamp' => $timestamp,\n ],\n ]);\n\n // Create note\n $note = $this->getNewInstance()->crm()->objects()->basicApi()->create('note', $noteInput);\n\n $this->getNewInstance()->crm()->objects()->associationsApi()->create(\n 'note',\n $note->getId(),\n $this->getNoteObject($noteObject),\n $objectId,\n $this->getNoteAssociationType($noteObject),\n );\n\n return $note->getId();\n } catch (\\Exception $e) {\n $this->log->error('[Hubspot] Failed to create note', [\n 'objectId' => $objectId,\n 'noteObject' => $noteObject->getObjectType(),\n 'reason' => $e->getMessage(),\n ]);\n\n \\Sentry::captureException($e);\n }\n\n return null;\n }\n\n public function updateEngagement(string $objectId, array $engagement, array $metadata): void\n {\n $this->getInstance()->engagements()->update($objectId, $engagement, $metadata);\n }\n\n public function getEngagementData(string $engagementId): array\n {\n $engagement = $this->getInstance()->engagements()->get($engagementId);\n\n return $engagement->toArray();\n }\n\n public function createEngagement(array $engagement, array $associations, array $metadata): Response\n {\n return $this->getInstance()\n ->engagements()\n ->create($engagement, $associations, $metadata);\n }\n\n public function isUnauthorizedException(\\Exception $e): bool\n {\n // Check for specific HubSpot API exception types first\n if ($e instanceof BadRequest) {\n // BadRequest can contain 401 status codes\n return $e->getCode() === 401;\n }\n\n // Check for HTTP client exceptions with status codes\n if ($e instanceof \\GuzzleHttp\\Exception\\RequestException && $e->hasResponse()) {\n $response = $e->getResponse();\n if ($response !== null) {\n return $response->getStatusCode() === 401;\n }\n }\n\n // Check for Guzzle HTTP exceptions\n if ($e instanceof \\GuzzleHttp\\Exception\\ClientException) {\n return $e->getCode() === 401;\n }\n\n // Fallback to string matching as last resort, but be more specific\n $message = strtolower($e->getMessage());\n\n return str_contains($message, '401 unauthorized') ||\n str_contains($message, 'http 401') ||\n str_contains($message, 'status code 401') ||\n (preg_match('/\\b401\\b/', $message) && str_contains($message, 'unauthorized'));\n }\n\n /**\n * Validates and refreshes the access token if needed before API requests.\n * This ensures long-running processes don't fail due to token expiration.\n *\n * @throws SocialAccountTokenInvalidException\n */\n public function ensureValidToken(): void\n {\n if ($this->oauthAccount === null) {\n return;\n }\n\n $newToken = $this->tokenManager->ensureValidToken($this->oauthAccount);\n if ($newToken !== null) {\n $this->accessToken = $newToken;\n }\n }\n\n public function getConfig()\n {\n return $this->config;\n }\n\n // returns only active (archived=false)\n public function getOwners(): array\n {\n return $this->getNewInstance()->crm()->owners()->getAll();\n }\n\n /**\n * @param bool $archived\n *\n * @return array<Owner>|[]\n */\n public function getOwnersArchived(bool $archived = true): array\n {\n $endpoint = '/crm/v3/owners';\n $queryParams = [\n 'archived' => $archived ? 'true' : 'false',\n ];\n $queryString = http_build_query($queryParams);\n\n $owners = [];\n\n try {\n $response = $this->makeRequest(endpoint: $endpoint, queryString: $queryString);\n $responseData = $response?->toArray();\n\n foreach ($responseData['results'] as $result) {\n try {\n $owners[] = Owner::create($result);\n } catch (Throwable $e) {\n $this->log->error('[HubSpot] Failed to process owner data', [\n 'result' => $result,\n 'error' => $e->getMessage(),\n ]);\n\n continue;\n }\n }\n } catch (Throwable $e) {\n $this->log->error('HubSpot] Failed to fetch owners', [\n 'archived' => $archived,\n 'error' => $e->getMessage(),\n ]);\n\n return [];\n }\n\n return $owners;\n }\n\n public function getMeeting(string $engagementId): ObjectWithAssociations\n {\n return $this->getNewInstance()->crm()->objects()->basicApi()\n ->getById('meeting', $engagementId, null, 'contact,company,deal');\n }\n\n public function deleteEngagement(string $engagementId): void\n {\n $this->getInstance()->engagements()->delete((int) $engagementId);\n }\n\n public function getAssociationsData(array $ids, string $fromObject, string $toObject): array\n {\n $associationData = [];\n $idChunks = array_chunk($ids, self::ASSOCIATIONS_BATCH_SIZE_LIMIT);\n\n foreach ($idChunks as $idChunk) {\n try {\n $batchInput = new \\HubSpot\\Client\\Crm\\Associations\\Model\\BatchInputPublicObjectId();\n $batchInput->setInputs(array_map(function ($id) {\n $publicObjectId = new \\HubSpot\\Client\\Crm\\Associations\\Model\\PublicObjectId();\n $publicObjectId->setId($id);\n\n return $publicObjectId;\n }, $idChunk));\n\n $associatedObjectsData = $this\n ->getNewInstance()\n ->crm()\n ->associations()\n ->batchApi()\n ->read($fromObject, $toObject, $batchInput);\n\n if ($associatedObjectsData instanceof \\HubSpot\\Client\\Crm\\Associations\\Model\\BatchResponsePublicAssociationMulti) {\n foreach ($associatedObjectsData->getResults() as $association) {\n $from = $association->getFrom()->getId();\n $toAssociations = $association->getTo();\n\n if (! empty($toAssociations)) {\n $associationData[$from] = array_map(function ($item) {\n return $item->getId();\n }, $toAssociations);\n }\n }\n }\n// } catch (RateLimitException $e) {\n// throw $e;\n } catch (\\Exception $e) {\n $this->log->error('[Hubspot] Failed to fetch associations', [\n 'from_object' => $fromObject,\n 'to_object' => $toObject,\n 'reason' => $e->getMessage(),\n ]);\n }\n }\n\n return $associationData;\n }\n\n /**\n * @throws \\Exception\n */\n private function getNoteAssociationType(NoteObject $noteObject): string\n {\n return match($noteObject) {\n NoteObject::Opportunity => 'note_to_deal',\n NoteObject::Lead, NoteObject::Contact => 'note_to_contact', // or 'note_to_lead' if your portal supports it\n NoteObject::Account => 'note_to_company',\n NoteObject::Call, NoteObject::Event => throw new \\Exception('Not supported'),\n };\n }\n\n /**\n * @throws \\Exception\n */\n private function getNoteObject(NoteObject $noteObject): string\n {\n return match($noteObject) {\n NoteObject::Opportunity => 'deal',\n NoteObject::Lead, NoteObject::Contact => 'contact',\n NoteObject::Account => 'company',\n NoteObject::Call, NoteObject::Event => throw new \\Exception('Not supported'),\n };\n }\n\n public function addAssociations(string $objectType, string $associationType, array $payload): Response\n {\n $endpoint = \"/crm/v4/associations/$objectType/$associationType/batch/create\";\n\n return $this->makeRequest($endpoint, 'POST', $payload);\n }\n\n public function removeAssociations(string $objectType, string $associationType, array $payload): Response\n {\n $endpoint = \"/crm/v4/associations/$objectType/$associationType/batch/archive\";\n\n return $this->makeRequest($endpoint, 'POST', $payload);\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Services\\Crm\\Hubspot;\n\nuse HubSpot\\Client\\Crm\\Deals\\ApiException as DealApiException;\nuse HubSpot\\Client\\Crm\\Contacts\\ApiException as ContactApiException;\nuse HubSpot\\Client\\Crm\\Companies\\ApiException as CompanyApiException;\nuse HubSpot\\Client\\Crm\\Contacts\\Model\\SimplePublicObjectWithAssociations as ContactsWithAssociations;\nuse HubSpot\\Client\\Crm\\Companies\\Model\\SimplePublicObjectWithAssociations as CompaniesWithAssociations;\nuse HubSpot\\Client\\Crm\\Deals\\Model\\SimplePublicObjectWithAssociations as DealWithAssociations;\nuse HubSpot\\Client\\Crm\\Objects\\Model\\SimplePublicObjectInput;\nuse HubSpot\\Client\\Crm\\Objects\\Model\\SimplePublicObjectWithAssociations as ObjectWithAssociations;\nuse HubSpot\\Client\\Crm\\Pipelines\\Model\\Error;\nuse HubSpot\\Client\\Crm\\Pipelines\\Model\\PipelineStage;\nuse HubSpot\\Client\\Crm\\Properties\\Model\\Property;\nuse HubSpot\\Discovery\\Discovery;\nuse Jiminny\\Exceptions\\CrmException;\nuse Jiminny\\Exceptions\\RateLimitException;\nuse Jiminny\\Exceptions\\SocialAccountTokenInvalidException;\nuse Jiminny\\Jobs\\Crm\\NoteObject;\nuse Jiminny\\Models\\Crm\\Field;\nuse Jiminny\\Services\\Crm\\BaseClient;\nuse Jiminny\\Services\\Crm\\Hubspot\\DTO\\Response\\Owner;\nuse Jiminny\\Services\\SocialAccountService;\nuse SevenShores\\Hubspot\\Exceptions\\BadRequest;\nuse SevenShores\\Hubspot\\Exceptions\\HubspotException;\nuse SevenShores\\Hubspot\\Factory;\nuse SevenShores\\Hubspot\\Http\\Response;\nuse Jiminny\\Services\\Crm\\Hubspot\\Pagination\\HubspotPaginationService;\nuse Illuminate\\Support\\Facades\\Redis;\nuse Throwable;\n\n/**\n * @phpstan-type CrmFieldOption array{id:string, label:string, value?:string}\n */\nclass Client extends BaseClient implements HubspotClientInterface\n{\n public const string MIN_API_VERSION = '2';\n\n public const string BASE_URL = 'https://api.hubapi.com';\n\n public const int ASSOCIATIONS_BATCH_SIZE_LIMIT = 1000;\n\n private HubspotPaginationService $paginationService;\n private HubspotTokenManager $tokenManager;\n\n public function __construct(\n SocialAccountService $socialAccountService,\n HubspotPaginationService $paginationService,\n HubspotTokenManager $tokenManager\n ) {\n parent::__construct($socialAccountService);\n $this->paginationService = $paginationService;\n $this->tokenManager = $tokenManager;\n\n $this->setBaseUrl(self::BASE_URL);\n $this->setVersion(self::MIN_API_VERSION);\n }\n\n /**\n * Reacts to a rate limits (429) from HubSpot by translating it\n * into a RateLimitException carrying retry_after.\n *\n * Wrap any outbound HubSpot call (SDK or raw HTTP) like:\n *\n * $this->executeRequest(fn () => $this->getNewInstance()->crm()->...);\n *\n * @template T\n * @param callable(): T $apiCall\n * @return T\n *\n * @throws RateLimitException\n */\n private function executeRequest(callable $apiCall)\n {\n $cacheKey = $this->getRateLimitCacheKey();\n\n $cachedRetryAfter = Redis::get($cacheKey);\n if (is_string($cachedRetryAfter) && is_numeric($cachedRetryAfter)) {\n throw new RateLimitException(\n 'Hubspot rate limit (cached circuit-breaker)',\n (int) $cachedRetryAfter,\n );\n }\n\n try {\n return $apiCall();\n } catch (Throwable $e) {\n if ($this->isHubspotRateLimit($e)) {\n $retryAfter = $this->parseRetryAfter($e);\n\n Redis::setex($cacheKey, $retryAfter, (string) $retryAfter);\n\n $this->log->warning('[Hubspot] Received 429 from API', [\n 'team_id' => $this->config->team_id,\n 'config_id' => $this->config->getId(),\n 'retry_after' => $retryAfter,\n 'message' => $this->parseMessage($e),\n 'reason' => $e->getMessage(),\n ]);\n\n throw new RateLimitException('Hubspot returned 429', $retryAfter, $e);\n }\n\n throw $e;\n }\n }\n\n private function getRateLimitCacheKey(): string\n {\n return sprintf('hubspot:ratelimit:portal:%d', $this->config->getId());\n }\n\n public function isHubspotRateLimit(Throwable $e): bool\n {\n if ($e instanceof BadRequest\n || $e instanceof DealApiException\n || $e instanceof ContactApiException\n || $e instanceof CompanyApiException\n || $e instanceof \\GuzzleHttp\\Exception\\RequestException\n ) {\n return (int) $e->getCode() === 429;\n }\n\n return false;\n }\n\n public function parseRetryAfter(Throwable $e): int\n {\n if (method_exists($e, 'getResponseHeaders')) {\n $headers = $e->getResponseHeaders() ?: [];\n $value = $headers['Retry-After'] ?? $headers['retry-after'] ?? null;\n if (is_array($value)) {\n $value = $value[0] ?? null;\n }\n if (is_numeric($value)) {\n return (int) $value;\n }\n }\n\n $message = strtolower($this->parseMessage($e));\n\n if (str_contains($message, 'daily')) {\n return 600;\n }\n if (str_contains($message, 'ten secondly')) {\n return 10;\n }\n if (str_contains($message, 'secondly')) {\n return 1;\n }\n\n $this->log->warning('[Hubspot] No retry-after header or known message, using default', [\n 'exception_class' => get_class($e),\n 'message' => $message,\n ]);\n\n return 10;\n }\n\n public function parseMessage(Throwable $e): string\n {\n if (! method_exists($e, 'getResponseBody')) {\n return '';\n }\n\n $body = $e->getResponseBody();\n if (is_string($body)) {\n $body = json_decode($body, true) ?? [];\n }\n\n if (! is_array($body)) {\n return '';\n }\n\n return (string) ($body['message'] ?? '');\n }\n\n public function getMinimumApiVersion(): string\n {\n return self::MIN_API_VERSION;\n }\n\n public function getInstance(): Factory\n {\n return new Factory([\n 'key' => $this->accessToken,\n 'oauth2' => true,\n 'base_url' => $this->baseUrl,\n ]);\n }\n\n public function getNewInstance(): Discovery\n {\n return \\HubSpot\\Factory::createWithAccessToken($this->accessToken);\n }\n\n /**\n * Secondly and daily limits for Hubspot API\n *\n * Product Tier: Free & Starter | Professional & Enterprise | API add-on (any tier)\n * Burst: 100/10 seconds | 150/10 seconds | 200/10 seconds\n * Daily: 250,000 | 500,000 | 1,000,000\n *\n * Official documentation states: The search endpoints are rate limited to five requests per second.\n * Since with 5 RPS were still hitting secondly rate limits we lowered it to 4\n */\n public function getPaginatedData(array $payload, string $type, int $offset = 0): array\n {\n $total = 0;\n $lastId = null;\n $rows = [];\n foreach ($this->getPaginatedDataGenerator($payload, $type, $offset, $total, $lastId) as $row) {\n $rows[] = $row;\n }\n\n return ['results' => $rows, 'total' => $total, 'last_record' => $lastId];\n }\n\n /**\n * @throws HubspotException\n * @throws SocialAccountTokenInvalidException\n * @throws BadRequest\n */\n public function getPaginatedDataGenerator(\n array $payload,\n string $type,\n int $offset = 0,\n int &$total = 0,\n ?string &$lastRecordId = null\n ): \\Generator {\n return $this->paginationService->getPaginatedDataGenerator(\n $this,\n $payload,\n $type,\n $offset,\n $total,\n $lastRecordId\n );\n }\n\n /**\n * Execute a search request against HubSpot CRM objects with rate limiting.\n *\n * @param string $objectType The object type ('deals', 'companies', 'contacts', 'calls')\n * @param array<string, mixed> $payload The search payload with filters, sorts, properties, etc.\n * @return array The search response with 'results', 'total', 'paging' keys\n * @throws RateLimitException When rate limit is hit\n * @throws HubspotException On API errors\n */\n public function search(string $objectType, array $payload): array\n {\n $endpoint = self::BASE_URL . \"/crm/v3/objects/{$objectType}/search\";\n\n return $this->executeRequest(function () use ($endpoint, $payload) {\n $response = $this->getInstance()->getClient()->request('POST', $endpoint, ['json' => $payload]);\n\n return $response->toArray();\n });\n }\n\n /**\n * @throws DealApiException\n * @throws CrmException\n */\n public function getOpportunityById(string $crmId, array $fields): array\n {\n try {\n// $deal = $this->executeRequest(fn () => $this->getNewInstance()->crm()->deals()->basicApi()->getById(\n $deal = $this->getNewInstance()->crm()->deals()->basicApi()->getById(\n $crmId,\n implode(',', $fields),\n 'companies,contacts'\n );\n } catch (DealApiException $e) {\n $this->log->info('[Hubspot] Failed to fetch opportunity', [\n 'crm_id' => $crmId,\n 'reason' => $e->getMessage(),\n ]);\n\n throw $e;\n }\n\n if (! $deal instanceof DealWithAssociations) {\n throw new CrmException('Deal not found');\n }\n\n return [\n 'id' => $deal->getId(),\n 'properties' => $deal->getProperties(),\n 'associations' => $deal->getAssociations(),\n ];\n }\n\n /**\n * Generic batch read method for HubSpot objects\n *\n * @param string $objectType The object type ('deals', 'companies', 'contacts')\n * @param array<string> $crmIds Array of HubSpot object IDs (max 100)\n * @param array<string> $fields Array of property names to fetch\n *\n * @return array<string, array> Array keyed by CRM ID with object data\n */\n private function batchReadObjects(string $objectType, array $crmIds, array $fields): array\n {\n if (empty($crmIds)) {\n return [];\n }\n\n $this->validateBatchSize($objectType, $crmIds);\n $this->ensureValidToken();\n\n try {\n $batchConfig = $this->createBatchConfiguration($objectType);\n $batchReadRequest = $this->prepareBatchRequest($batchConfig, $crmIds, $fields);\n $response = $batchConfig['api']->read($batchReadRequest);\n\n $this->validateApiResponse($response, $objectType);\n\n $results = $this->processApiResults($response);\n $this->logBatchResults($objectType, $crmIds, $results);\n\n return $results;\n } catch (\\Throwable $e) {\n $this->handleBatchError($e, $objectType, $crmIds);\n }\n }\n\n private function validateBatchSize(string $objectType, array $crmIds): void\n {\n if (count($crmIds) > 100) {\n throw new \\InvalidArgumentException(\"Batch size cannot exceed 100 {$objectType}\");\n }\n }\n\n private function createBatchConfiguration(string $objectType): array\n {\n $configurations = [\n 'deals' => [\n 'batchReadRequest' => new \\HubSpot\\Client\\Crm\\Deals\\Model\\BatchReadInputSimplePublicObjectId(),\n 'inputClass' => \\HubSpot\\Client\\Crm\\Deals\\Model\\SimplePublicObjectId::class,\n 'api' => $this->getNewInstance()->crm()->deals()->batchApi(),\n ],\n 'companies' => [\n 'batchReadRequest' => new \\HubSpot\\Client\\Crm\\Companies\\Model\\BatchReadInputSimplePublicObjectId(),\n 'inputClass' => \\HubSpot\\Client\\Crm\\Companies\\Model\\SimplePublicObjectId::class,\n 'api' => $this->getNewInstance()->crm()->companies()->batchApi(),\n ],\n 'contacts' => [\n 'batchReadRequest' => new \\HubSpot\\Client\\Crm\\Contacts\\Model\\BatchReadInputSimplePublicObjectId(),\n 'inputClass' => \\HubSpot\\Client\\Crm\\Contacts\\Model\\SimplePublicObjectId::class,\n 'api' => $this->getNewInstance()->crm()->contacts()->batchApi(),\n ],\n ];\n\n if (! isset($configurations[$objectType])) {\n throw new \\InvalidArgumentException(\"Unsupported object type: {$objectType}\");\n }\n\n return $configurations[$objectType];\n }\n\n private function prepareBatchRequest(array $batchConfig, array $crmIds, array $fields): object\n {\n $batchReadRequest = $batchConfig['batchReadRequest'];\n $inputClass = $batchConfig['inputClass'];\n\n $inputs = array_map(function ($crmId) use ($inputClass) {\n $input = new $inputClass();\n $input->setId($crmId);\n\n return $input;\n }, $crmIds);\n\n $batchReadRequest->setInputs($inputs);\n $batchReadRequest->setProperties($fields);\n\n return $batchReadRequest;\n }\n\n private function validateApiResponse($response, string $objectType): void\n {\n if (! $response) {\n throw new CrmException(\"HubSpot API returned null response for {$objectType} batch read\");\n }\n }\n\n private function processApiResults($response): array\n {\n $results = [];\n $responseResults = $response->getResults();\n\n if ($responseResults) {\n foreach ($responseResults as $object) {\n if ($object && $object->getId()) {\n $results[$object->getId()] = [\n 'id' => $object->getId(),\n 'properties' => $object->getProperties() ?: [],\n ];\n }\n }\n }\n\n return $results;\n }\n\n private function logBatchResults(string $objectType, array $crmIds, array $results): void\n {\n $this->log->info(\"[HubSpot] Batch fetched {$objectType}\", [\n 'requested_count' => count($crmIds),\n 'returned_count' => count($results),\n 'crm_ids' => $crmIds,\n ]);\n }\n\n private function handleBatchError(\\Throwable $e, string $objectType, array $crmIds): void\n {\n $errorMessage = $e->getMessage() ?: 'Unknown error';\n $errorTrace = $e->getTraceAsString() ?: 'No trace available';\n\n $this->log->error(\"[HubSpot] Failed to batch fetch {$objectType}\", [\n 'crm_ids' => $crmIds,\n 'error' => $errorMessage,\n 'trace' => $errorTrace,\n ]);\n\n throw new CrmException(\"Failed to batch fetch {$objectType}: \" . $errorMessage);\n }\n\n /**\n * Batch read multiple opportunities by their CRM IDs\n *\n * @param array<string> $crmIds Array of HubSpot deal IDs (max 100)\n * @param array<string> $fields Array of property names to fetch\n *\n * @return array<string, array> Array keyed by CRM ID with opportunity data\n */\n public function getOpportunitiesByIds(array $crmIds, array $fields): array\n {\n return $this->batchReadObjects('deals', $crmIds, $fields);\n }\n\n /**\n * Batch read multiple companies by their CRM IDs\n *\n * @param array<string> $crmIds Array of HubSpot company IDs (max 100)\n * @param array<string> $fields Array of property names to fetch\n *\n * @return array<string, array> Array keyed by CRM ID with company data\n */\n public function getCompaniesByIds(array $crmIds, array $fields): array\n {\n return $this->batchReadObjects('companies', $crmIds, $fields);\n }\n\n /**\n * Batch read multiple contacts by their CRM IDs\n *\n * @param array<string> $crmIds Array of HubSpot contact IDs (max 100)\n * @param array<string> $fields Array of property names to fetch\n *\n * @return array<string, array> Array keyed by CRM ID with contact data\n */\n public function getContactsByIds(array $crmIds, array $fields): array\n {\n return $this->batchReadObjects('contacts', $crmIds, $fields);\n }\n\n /**\n * @throws CompanyApiException\n * @throws CrmException\n */\n public function getAccountById(string $crmId, array $fields): array\n {\n try {\n $company = $this->getNewInstance()->crm()->companies()->basicApi()->getById(\n $crmId,\n implode(',', $fields),\n );\n } catch (CompanyApiException $e) {\n $this->log->info('[Hubspot] Failed to fetch account', [\n 'crm_id' => $crmId,\n 'reason' => $e->getMessage(),\n ]);\n\n throw $e;\n }\n\n if (! $company instanceof CompaniesWithAssociations) {\n throw new CrmException('Account not found');\n }\n\n return [\n 'id' => $company->getId(),\n 'properties' => $company->getProperties(),\n ];\n }\n\n /**\n * @throws ContactApiException\n * @throws CrmException\n */\n public function getContactById(string $crmId, array $fields): array\n {\n try {\n $contact = $this->getNewInstance()->crm()->contacts()->basicApi()->getById(\n $crmId,\n implode(',', $fields)\n );\n } catch (ContactApiException $e) {\n $this->log->info('[Hubspot] Failed to fetch contact', [\n 'crm_id' => $crmId,\n 'reason' => $e->getMessage(),\n ]);\n\n throw $e;\n }\n\n if (! $contact instanceof ContactsWithAssociations) {\n throw new CrmException('Contact not found');\n }\n\n return [\n 'id' => $contact->getId(),\n 'properties' => $contact->getProperties(),\n ];\n }\n\n /**\n * This is email search request that Hubspot offers as GET (more generous quota)\n */\n public function getContactByEmail(string $email, array $fields = []): array\n {\n try {\n $contact = $this->getNewInstance()->crm()->contacts()->basicApi()->getById(\n $email,\n implode(',', $fields),\n null,\n false,\n 'email'\n );\n\n return [\n 'id' => $contact->getId(),\n 'properties' => $contact->getProperties(),\n ];\n } catch (ContactApiException $e) {\n $this->log->info('[Hubspot] Failed to fetch contact', [\n 'email' => $email,\n 'reason' => $e->getMessage(),\n ]);\n\n return [];\n }\n }\n\n /**\n * @throws CrmException\n */\n public function fetchProperty(string $objectType, string $propertyId): Property\n {\n $result = $this->getNewInstance()->crm()->properties()->coreApi()->getByName($objectType, $propertyId);\n\n if (! $result instanceof Property) {\n $this->log->error('[Hubspot] Failed to fetch property', [\n 'object_type' => $objectType,\n 'property_id' => $propertyId,\n 'reason' => $result->getMessage(),\n ]);\n\n throw new CrmException('Failed to fetch property');\n }\n\n return $result;\n }\n\n /**\n * @return array<CrmFieldOption>\n */\n public function fetchPropertyOptions(string $objectType, string $propertyId): array\n {\n /** @var array<CrmFieldOption> */\n return $this->fetchProperty($objectType, $propertyId)->getOptions();\n }\n\n /**\n * @return array<array{id:string, label:string, deleted:bool}>\n */\n public function fetchCallDispositions(): array\n {\n /** @var Response $response */\n $response = $this->getInstance()->engagements()->getCallDispositions();\n\n /**\n * @var array<array{\n * id:string,\n * label:string,\n * deleted: bool\n * }>\n */\n return $response->toArray();\n }\n\n /**\n * @return array<CrmFieldOption>\n */\n public function fetchOpportunityPipelineStages(): array\n {\n $stages = [];\n $apiResponse = $this->getNewInstance()->crm()->pipelines()->pipelinesApi()->getAll('deals');\n\n if ($apiResponse instanceof Error) {\n $this->log->error('[Hubspot] Failed to fetch opportunity pipelines', [\n 'reason' => $apiResponse->getMessage(),\n ]);\n\n return [];\n }\n\n foreach ($apiResponse->getResults() as $pipeline) {\n $pipelineStages = array_map(\n static function (PipelineStage $stage) {\n return [\n 'id' => $stage->getId(),\n 'label' => $stage->getLabel(),\n ];\n },\n $pipeline->getStages()\n );\n\n $stages = array_merge($stages, $pipelineStages);\n }\n\n return $stages;\n }\n\n public function fetchOpportunityPipelines(): array\n {\n $pipelines = [];\n\n try {\n $apiResponse = $this->makeRequest('/crm/v3/pipelines/deals');\n } catch (\\Exception $e) {\n $this->log->info('[Hubspot] Failed to fetch opportunity pipelines', [\n 'reason' => $e->getMessage(),\n ]);\n\n return [];\n }\n\n $response = $apiResponse->toArray();\n\n foreach ($response['results'] as $pipeline) {\n $pipelines[] = [\n 'id' => $pipeline['id'],\n 'label' => $pipeline['label'],\n ];\n }\n\n return $pipelines;\n }\n\n /**\n * @return array<CrmFieldOption>\n */\n public function fetchMeetingOutcomeFieldOptions(Field $field): array\n {\n return $field->getCrmProviderId() === 'meetingOutcome'\n ? $this->fetchMeetingOutcomeTypes()\n : $this->fetchCallActivityTypes();\n }\n\n public function fetchMeetingOutcomeTypes(): array\n {\n return $this->extractMeetingTypeOptions(\n 'https://api.hubapi.com/crm/v3/properties/meeting/hs_meeting_outcome'\n );\n }\n\n public function fetchCallActivityTypes(): array\n {\n return $this->extractMeetingTypeOptions(\n 'https://api.hubapi.com/crm/v3/properties/call/hs_activity_type'\n );\n }\n\n private function extractMeetingTypeOptions(string $endpoint): array\n {\n /** @var Response $response */\n $response = $this->getInstance()\n ->getClient()\n ->request('GET', $endpoint);\n\n /**\n * @var array<array{\n * value: string,\n * label: string,\n * displayOrder: int\n * }> $optionData\n */\n $optionData = $response->toArray()['options'] ?? [];\n\n $options = [];\n foreach ($optionData as $item) {\n $options[] = [\n 'id' => $item['value'],\n 'value' => $item['value'],\n 'label' => $item['label'],\n 'display_order' => $item['displayOrder'],\n ];\n }\n\n return $options;\n }\n\n /**\n * @return array<CrmFieldOption>\n */\n public function fetchDispositionFieldOptions(): array\n {\n $options = [];\n\n $dispositions = $this->fetchCallDispositions();\n\n foreach ($dispositions as $disposition) {\n if ($disposition['deleted'] !== false) {\n continue;\n }\n\n $option['value'] = $disposition['id'];\n $option['id'] = $disposition['id'];\n $option['label'] = $disposition['label'];\n\n $options[] = $option;\n }\n\n return $options;\n }\n\n /**\n * @return array<CrmFieldOption>\n */\n public function fetchOpportunityFieldOptions(Field $field): array\n {\n if ($field->isStageField()) {\n return $this->fetchOpportunityPipelineStages();\n }\n\n if ($field->isPipelineField()) {\n return $this->fetchOpportunityPipelines();\n }\n\n return $this->fetchPropertyOptions('deals', $field->getCrmProviderId());\n }\n\n /**\n * @throws BadRequest\n * @throws HubspotException\n */\n public function makeRequest(string $endpoint, $method = 'GET', $payload = [], ?string $queryString = null)\n {\n $endpoint = self::BASE_URL . $endpoint;\n\n if ($method === 'GET') {\n return $this->getInstance()->getClient()?->request(\n method: $method,\n endpoint: $endpoint,\n query_string: $queryString\n );\n } else {\n return $this->getInstance()->getClient()->request($method, $endpoint, [\n 'json' => ($payload),\n ]);\n }\n }\n\n /**\n * @throws BadRequest\n * @throws HubspotException\n */\n public function createMeeting(array $payload): Response\n {\n $endpoint = '/crm/v3/objects/meetings';\n\n return $this->makeRequest($endpoint, 'POST', $payload);\n }\n\n /**\n * @throws BadRequest\n * @throws HubspotException\n */\n public function updateMeeting(string $meetingId, array $payload): Response\n {\n $endpoint = '/crm/v3/objects/meetings/' . $meetingId;\n\n return $this->makeRequest($endpoint, 'PATCH', $payload);\n }\n\n /**\n * @throws \\Exception\n */\n public function createNote(\n string $body,\n string $ownerId,\n int $timestamp,\n string $objectId,\n NoteObject $noteObject\n ): ?string {\n try {\n $noteInput = new SimplePublicObjectInput([\n 'properties' => [\n 'hs_note_body' => $body,\n 'hubspot_owner_id' => $ownerId,\n 'hs_timestamp' => $timestamp,\n ],\n ]);\n\n // Create note\n $note = $this->getNewInstance()->crm()->objects()->basicApi()->create('note', $noteInput);\n\n $this->getNewInstance()->crm()->objects()->associationsApi()->create(\n 'note',\n $note->getId(),\n $this->getNoteObject($noteObject),\n $objectId,\n $this->getNoteAssociationType($noteObject),\n );\n\n return $note->getId();\n } catch (\\Exception $e) {\n $this->log->error('[Hubspot] Failed to create note', [\n 'objectId' => $objectId,\n 'noteObject' => $noteObject->getObjectType(),\n 'reason' => $e->getMessage(),\n ]);\n\n \\Sentry::captureException($e);\n }\n\n return null;\n }\n\n public function updateEngagement(string $objectId, array $engagement, array $metadata): void\n {\n $this->getInstance()->engagements()->update($objectId, $engagement, $metadata);\n }\n\n public function getEngagementData(string $engagementId): array\n {\n $engagement = $this->getInstance()->engagements()->get($engagementId);\n\n return $engagement->toArray();\n }\n\n public function createEngagement(array $engagement, array $associations, array $metadata): Response\n {\n return $this->getInstance()\n ->engagements()\n ->create($engagement, $associations, $metadata);\n }\n\n public function isUnauthorizedException(\\Exception $e): bool\n {\n // Check for specific HubSpot API exception types first\n if ($e instanceof BadRequest) {\n // BadRequest can contain 401 status codes\n return $e->getCode() === 401;\n }\n\n // Check for HTTP client exceptions with status codes\n if ($e instanceof \\GuzzleHttp\\Exception\\RequestException && $e->hasResponse()) {\n $response = $e->getResponse();\n if ($response !== null) {\n return $response->getStatusCode() === 401;\n }\n }\n\n // Check for Guzzle HTTP exceptions\n if ($e instanceof \\GuzzleHttp\\Exception\\ClientException) {\n return $e->getCode() === 401;\n }\n\n // Fallback to string matching as last resort, but be more specific\n $message = strtolower($e->getMessage());\n\n return str_contains($message, '401 unauthorized') ||\n str_contains($message, 'http 401') ||\n str_contains($message, 'status code 401') ||\n (preg_match('/\\b401\\b/', $message) && str_contains($message, 'unauthorized'));\n }\n\n /**\n * Validates and refreshes the access token if needed before API requests.\n * This ensures long-running processes don't fail due to token expiration.\n *\n * @throws SocialAccountTokenInvalidException\n */\n public function ensureValidToken(): void\n {\n if ($this->oauthAccount === null) {\n return;\n }\n\n $newToken = $this->tokenManager->ensureValidToken($this->oauthAccount);\n if ($newToken !== null) {\n $this->accessToken = $newToken;\n }\n }\n\n public function getConfig()\n {\n return $this->config;\n }\n\n // returns only active (archived=false)\n public function getOwners(): array\n {\n return $this->getNewInstance()->crm()->owners()->getAll();\n }\n\n /**\n * @param bool $archived\n *\n * @return array<Owner>|[]\n */\n public function getOwnersArchived(bool $archived = true): array\n {\n $endpoint = '/crm/v3/owners';\n $queryParams = [\n 'archived' => $archived ? 'true' : 'false',\n ];\n $queryString = http_build_query($queryParams);\n\n $owners = [];\n\n try {\n $response = $this->makeRequest(endpoint: $endpoint, queryString: $queryString);\n $responseData = $response?->toArray();\n\n foreach ($responseData['results'] as $result) {\n try {\n $owners[] = Owner::create($result);\n } catch (Throwable $e) {\n $this->log->error('[HubSpot] Failed to process owner data', [\n 'result' => $result,\n 'error' => $e->getMessage(),\n ]);\n\n continue;\n }\n }\n } catch (Throwable $e) {\n $this->log->error('HubSpot] Failed to fetch owners', [\n 'archived' => $archived,\n 'error' => $e->getMessage(),\n ]);\n\n return [];\n }\n\n return $owners;\n }\n\n public function getMeeting(string $engagementId): ObjectWithAssociations\n {\n return $this->getNewInstance()->crm()->objects()->basicApi()\n ->getById('meeting', $engagementId, null, 'contact,company,deal');\n }\n\n public function deleteEngagement(string $engagementId): void\n {\n $this->getInstance()->engagements()->delete((int) $engagementId);\n }\n\n public function getAssociationsData(array $ids, string $fromObject, string $toObject): array\n {\n $associationData = [];\n $idChunks = array_chunk($ids, self::ASSOCIATIONS_BATCH_SIZE_LIMIT);\n\n foreach ($idChunks as $idChunk) {\n try {\n $batchInput = new \\HubSpot\\Client\\Crm\\Associations\\Model\\BatchInputPublicObjectId();\n $batchInput->setInputs(array_map(function ($id) {\n $publicObjectId = new \\HubSpot\\Client\\Crm\\Associations\\Model\\PublicObjectId();\n $publicObjectId->setId($id);\n\n return $publicObjectId;\n }, $idChunk));\n\n $associatedObjectsData = $this\n ->getNewInstance()\n ->crm()\n ->associations()\n ->batchApi()\n ->read($fromObject, $toObject, $batchInput);\n\n if ($associatedObjectsData instanceof \\HubSpot\\Client\\Crm\\Associations\\Model\\BatchResponsePublicAssociationMulti) {\n foreach ($associatedObjectsData->getResults() as $association) {\n $from = $association->getFrom()->getId();\n $toAssociations = $association->getTo();\n\n if (! empty($toAssociations)) {\n $associationData[$from] = array_map(function ($item) {\n return $item->getId();\n }, $toAssociations);\n }\n }\n }\n// } catch (RateLimitException $e) {\n// throw $e;\n } catch (\\Exception $e) {\n $this->log->error('[Hubspot] Failed to fetch associations', [\n 'from_object' => $fromObject,\n 'to_object' => $toObject,\n 'reason' => $e->getMessage(),\n ]);\n }\n }\n\n return $associationData;\n }\n\n /**\n * @throws \\Exception\n */\n private function getNoteAssociationType(NoteObject $noteObject): string\n {\n return match($noteObject) {\n NoteObject::Opportunity => 'note_to_deal',\n NoteObject::Lead, NoteObject::Contact => 'note_to_contact', // or 'note_to_lead' if your portal supports it\n NoteObject::Account => 'note_to_company',\n NoteObject::Call, NoteObject::Event => throw new \\Exception('Not supported'),\n };\n }\n\n /**\n * @throws \\Exception\n */\n private function getNoteObject(NoteObject $noteObject): string\n {\n return match($noteObject) {\n NoteObject::Opportunity => 'deal',\n NoteObject::Lead, NoteObject::Contact => 'contact',\n NoteObject::Account => 'company',\n NoteObject::Call, NoteObject::Event => throw new \\Exception('Not supported'),\n };\n }\n\n public function addAssociations(string $objectType, string $associationType, array $payload): Response\n {\n $endpoint = \"/crm/v4/associations/$objectType/$associationType/batch/create\";\n\n return $this->makeRequest($endpoint, 'POST', $payload);\n }\n\n public function removeAssociations(string $objectType, string $associationType, array $payload): Response\n {\n $endpoint = \"/crm/v4/associations/$objectType/$associationType/batch/archive\";\n\n return $this->makeRequest($endpoint, 'POST', $payload);\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":true,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"19","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"[2026-05-07 14:21:15] local.INFO: [Hubspot] DEBUG Getting headers {\n\"headers\":{\n\"Date\":[\"Thu,07 May 2026 14:21:15 GMT\"],\n \"Content-Type\":[\"application/json;charset=utf-8\"],\n \"Transfer-Encoding\":[\"chunked\"],\n \"Connection\":[\"keep-alive\"],\n \"CF-Ray\":[\"9f80deb8db60dc3a-SOF\"],\n \"CF-Cache-Status\":[\"DYNAMIC\"],\n \"Strict-Transport-Security\":[\"max-age=31536000; includeSubDomains; preload\"],\n \"Vary\":[\"origin,\n accept-encoding\"],\n \"access-control-allow-credentials\":[\"false\"],\n \"server-timing\":[\"hcid;desc=\\\"019e02d0-6fd8-7812-bdba-885b7ccb3ee3\\\",\n cfr;desc=\\\"9f80deb8e7c6dc3a-IAD\\\"\"],\n \"x-content-type-options\":[\"nosniff\"],\n \"x-hubspot-correlation-id\":[\"019e02d0-6fd8-7812-bdba-885b7ccb3ee3\"],\n \"Set-Cookie\":[\"__cf_bm=SIUrtdQgXVrik50pdqF6hZVYKhzTnQBidvMabeCtm0Y-1778163675-1.0.1.1-rI.ZggtDKxTge5zr8_2gbBfWMQQ.ufZEXDZyHz2mBUFdzdo2gTHEsOkXMSEShjK0hGYxNhUGM1ZoBpX7BcFZcHEjA7Cs_.SMUhUnd2nYjko; path=/; expires=Thu,\n 07-May-26 14:51:15 GMT; domain=.hubapi.com; HttpOnly; Secure; SameSite=None\"],\n \"Report-To\":[\"{\n\\\"endpoints\\\":[{\n\\\"url\\\":\\\"https:\\\\/\\\\/a.nel.cloudflare.com\\\\/report\\\\/v4?s=NYAlsVTP0fYm32qrSDjxYE4sd2RWRqiSp3wHsmdEgZlzoYdxI%2BIxVpHmsKn3O%2BKVA3mFIJ2m7YRECDGSM%2BW2IYTzo6FM4%2BdUIjURO8srzKSvJgZ%2BQ6R79arKQw3uHLlX\\\"}],\n\\\"group\\\":\\\"cf-nel\\\",\n\\\"max_age\\\":604800}\"],\n\"NEL\":[\"{\n\\\"success_fraction\\\":0.01,\n\\\"report_to\\\":\\\"cf-nel\\\",\n\\\"max_age\\\":604800}\"],\n\"Server\":[\"cloudflare\"]}} {\n\"correlation_id\":\"95236535-ec98-4541-b92a-adfa73b69eab\",\n\"trace_id\":\"c7ab8365-903f-46d4-9403-0e5b551e3545\"}","depth":4,"on_screen":true,"value":"[2026-05-07 14:21:15] local.INFO: [Hubspot] DEBUG Getting headers {\n\"headers\":{\n\"Date\":[\"Thu,07 May 2026 14:21:15 GMT\"],\n \"Content-Type\":[\"application/json;charset=utf-8\"],\n \"Transfer-Encoding\":[\"chunked\"],\n \"Connection\":[\"keep-alive\"],\n \"CF-Ray\":[\"9f80deb8db60dc3a-SOF\"],\n \"CF-Cache-Status\":[\"DYNAMIC\"],\n \"Strict-Transport-Security\":[\"max-age=31536000; includeSubDomains; preload\"],\n \"Vary\":[\"origin,\n accept-encoding\"],\n \"access-control-allow-credentials\":[\"false\"],\n \"server-timing\":[\"hcid;desc=\\\"019e02d0-6fd8-7812-bdba-885b7ccb3ee3\\\",\n cfr;desc=\\\"9f80deb8e7c6dc3a-IAD\\\"\"],\n \"x-content-type-options\":[\"nosniff\"],\n \"x-hubspot-correlation-id\":[\"019e02d0-6fd8-7812-bdba-885b7ccb3ee3\"],\n \"Set-Cookie\":[\"__cf_bm=SIUrtdQgXVrik50pdqF6hZVYKhzTnQBidvMabeCtm0Y-1778163675-1.0.1.1-rI.ZggtDKxTge5zr8_2gbBfWMQQ.ufZEXDZyHz2mBUFdzdo2gTHEsOkXMSEShjK0hGYxNhUGM1ZoBpX7BcFZcHEjA7Cs_.SMUhUnd2nYjko; path=/; expires=Thu,\n 07-May-26 14:51:15 GMT; domain=.hubapi.com; HttpOnly; Secure; SameSite=None\"],\n \"Report-To\":[\"{\n\\\"endpoints\\\":[{\n\\\"url\\\":\\\"https:\\\\/\\\\/a.nel.cloudflare.com\\\\/report\\\\/v4?s=NYAlsVTP0fYm32qrSDjxYE4sd2RWRqiSp3wHsmdEgZlzoYdxI%2BIxVpHmsKn3O%2BKVA3mFIJ2m7YRECDGSM%2BW2IYTzo6FM4%2BdUIjURO8srzKSvJgZ%2BQ6R79arKQw3uHLlX\\\"}],\n\\\"group\\\":\\\"cf-nel\\\",\n\\\"max_age\\\":604800}\"],\n\"NEL\":[\"{\n\\\"success_fraction\\\":0.01,\n\\\"report_to\\\":\\\"cf-nel\\\",\n\\\"max_age\\\":604800}\"],\n\"Server\":[\"cloudflare\"]}} {\n\"correlation_id\":\"95236535-ec98-4541-b92a-adfa73b69eab\",\n\"trace_id\":\"c7ab8365-903f-46d4-9403-0e5b551e3545\"}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
5549352204577814028
|
6378757150172121188
|
click
|
accessibility
|
NULL
|
Project: faVsco.js, menu
JY-20725-handle-HS-search Project: faVsco.js, menu
JY-20725-handle-HS-search-rate-limit, menu
Start Listening for PHP Debug Connections
HandleHubspotRateLimitTest
Run 'HandleHubspotRateLimitTest'
Debug 'HandleHubspotRateLimitTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
2
67
3
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Services\Crm\Hubspot;
use HubSpot\Client\Crm\Deals\ApiException as DealApiException;
use HubSpot\Client\Crm\Contacts\ApiException as ContactApiException;
use HubSpot\Client\Crm\Companies\ApiException as CompanyApiException;
use HubSpot\Client\Crm\Contacts\Model\SimplePublicObjectWithAssociations as ContactsWithAssociations;
use HubSpot\Client\Crm\Companies\Model\SimplePublicObjectWithAssociations as CompaniesWithAssociations;
use HubSpot\Client\Crm\Deals\Model\SimplePublicObjectWithAssociations as DealWithAssociations;
use HubSpot\Client\Crm\Objects\Model\SimplePublicObjectInput;
use HubSpot\Client\Crm\Objects\Model\SimplePublicObjectWithAssociations as ObjectWithAssociations;
use HubSpot\Client\Crm\Pipelines\Model\Error;
use HubSpot\Client\Crm\Pipelines\Model\PipelineStage;
use HubSpot\Client\Crm\Properties\Model\Property;
use HubSpot\Discovery\Discovery;
use Jiminny\Exceptions\CrmException;
use Jiminny\Exceptions\RateLimitException;
use Jiminny\Exceptions\SocialAccountTokenInvalidException;
use Jiminny\Jobs\Crm\NoteObject;
use Jiminny\Models\Crm\Field;
use Jiminny\Services\Crm\BaseClient;
use Jiminny\Services\Crm\Hubspot\DTO\Response\Owner;
use Jiminny\Services\SocialAccountService;
use SevenShores\Hubspot\Exceptions\BadRequest;
use SevenShores\Hubspot\Exceptions\HubspotException;
use SevenShores\Hubspot\Factory;
use SevenShores\Hubspot\Http\Response;
use Jiminny\Services\Crm\Hubspot\Pagination\HubspotPaginationService;
use Illuminate\Support\Facades\Redis;
use Throwable;
/**
* @phpstan-type CrmFieldOption array{id:string, label:string, value?:string}
*/
class Client extends BaseClient implements HubspotClientInterface
{
public const string MIN_API_VERSION = '2';
public const string BASE_URL = '[URL_WITH_CREDENTIALS] T
* @param callable(): T $apiCall
* @return T
*
* @throws RateLimitException
*/
private function executeRequest(callable $apiCall)
{
$cacheKey = $this->getRateLimitCacheKey();
$cachedRetryAfter = Redis::get($cacheKey);
if (is_string($cachedRetryAfter) && is_numeric($cachedRetryAfter)) {
throw new RateLimitException(
'Hubspot rate limit (cached circuit-breaker)',
(int) $cachedRetryAfter,
);
}
try {
return $apiCall();
} catch (Throwable $e) {
if ($this->isHubspotRateLimit($e)) {
$retryAfter = $this->parseRetryAfter($e);
Redis::setex($cacheKey, $retryAfter, (string) $retryAfter);
$this->log->warning('[Hubspot] Received 429 from API', [
'team_id' => $this->config->team_id,
'config_id' => $this->config->getId(),
'retry_after' => $retryAfter,
'message' => $this->parseMessage($e),
'reason' => $e->getMessage(),
]);
throw new RateLimitException('Hubspot returned 429', $retryAfter, $e);
}
throw $e;
}
}
private function getRateLimitCacheKey(): string
{
return sprintf('hubspot:ratelimit:portal:%d', $this->config->getId());
}
public function isHubspotRateLimit(Throwable $e): bool
{
if ($e instanceof BadRequest
|| $e instanceof DealApiException
|| $e instanceof ContactApiException
|| $e instanceof CompanyApiException
|| $e instanceof \GuzzleHttp\Exception\RequestException
) {
return (int) $e->getCode() === 429;
}
return false;
}
public function parseRetryAfter(Throwable $e): int
{
if (method_exists($e, 'getResponseHeaders')) {
$headers = $e->getResponseHeaders() ?: [];
$value = $headers['Retry-After'] ?? $headers['retry-after'] ?? null;
if (is_array($value)) {
$value = $value[0] ?? null;
}
if (is_numeric($value)) {
return (int) $value;
}
}
$message = strtolower($this->parseMessage($e));
if (str_contains($message, 'daily')) {
return 600;
}
if (str_contains($message, 'ten secondly')) {
return 10;
}
if (str_contains($message, 'secondly')) {
return 1;
}
$this->log->warning('[Hubspot] No retry-after header or known message, using default', [
'exception_class' => get_class($e),
'message' => $message,
]);
return 10;
}
public function parseMessage(Throwable $e): string
{
if (! method_exists($e, 'getResponseBody')) {
return '';
}
$body = $e->getResponseBody();
if (is_string($body)) {
$body = json_decode($body, true) ?? [];
}
if (! is_array($body)) {
return '';
}
return (string) ($body['message'] ?? '');
}
public function getMinimumApiVersion(): string
{
return self::MIN_API_VERSION;
}
public function getInstance(): Factory
{
return new Factory([
'key' => $this->accessToken,
'oauth2' => true,
'base_url' => $this->baseUrl,
]);
}
public function getNewInstance(): Discovery
{
return \HubSpot\Factory::createWithAccessToken($this->accessToken);
}
/**
* Secondly and daily limits for Hubspot API
*
* Product Tier: Free & Starter | Professional & Enterprise | API add-on (any tier)
* Burst: 100/10 seconds | 150/10 seconds | 200/10 seconds
* Daily: 250,000 | 500,000 | 1,000,000
*
* Official documentation states: The search endpoints are rate limited to five requests per second.
* Since with 5 RPS were still hitting secondly rate limits we lowered it to 4
*/
public function getPaginatedData(array $payload, string $type, int $offset = 0): array
{
$total = 0;
$lastId = null;
$rows = [];
foreach ($this->getPaginatedDataGenerator($payload, $type, $offset, $total, $lastId) as $row) {
$rows[] = $row;
}
return ['results' => $rows, 'total' => $total, 'last_record' => $lastId];
}
/**
* @throws HubspotException
* @throws SocialAccountTokenInvalidException
* @throws BadRequest
*/
public function getPaginatedDataGenerator(
array $payload,
string $type,
int $offset = 0,
int &$total = 0,
?string &$lastRecordId = null
): \Generator {
return $this->paginationService->getPaginatedDataGenerator(
$this,
$payload,
$type,
$offset,
$total,
$lastRecordId
);
}
/**
* Execute a search request against HubSpot CRM objects with rate limiting.
*
* @param string $objectType The object type ('deals', 'companies', 'contacts', 'calls')
* @param array<string, mixed> $payload The search payload with filters, sorts, properties, etc.
* @return array The search response with 'results', 'total', 'paging' keys
* @throws RateLimitException When rate limit is hit
* @throws HubspotException On API errors
*/
public function search(string $objectType, array $payload): array
{
$endpoint = self::BASE_URL . "/crm/v3/objects/{$objectType}/search";
return $this->executeRequest(function () use ($endpoint, $payload) {
$response = $this->getInstance()->getClient()->request('POST', $endpoint, ['json' => $payload]);
return $response->toArray();
});
}
/**
* @throws DealApiException
* @throws CrmException
*/
public function getOpportunityById(string $crmId, array $fields): array
{
try {
// $deal = $this->executeRequest(fn () => $this->getNewInstance()->crm()->deals()->basicApi()->getById(
$deal = $this->getNewInstance()->crm()->deals()->basicApi()->getById(
$crmId,
implode(',', $fields),
'companies,contacts'
);
} catch (DealApiException $e) {
$this->log->info('[Hubspot] Failed to fetch opportunity', [
'crm_id' => $crmId,
'reason' => $e->getMessage(),
]);
throw $e;
}
if (! $deal instanceof DealWithAssociations) {
throw new CrmException('Deal not found');
}
return [
'id' => $deal->getId(),
'properties' => $deal->getProperties(),
'associations' => $deal->getAssociations(),
];
}
/**
* Generic batch read method for HubSpot objects
*
* @param string $objectType The object type ('deals', 'companies', 'contacts')
* @param array<string> $crmIds Array of HubSpot object IDs (max 100)
* @param array<string> $fields Array of property names to fetch
*
* @return array<string, array> Array keyed by CRM ID with object data
*/
private function batchReadObjects(string $objectType, array $crmIds, array $fields): array
{
if (empty($crmIds)) {
return [];
}
$this->validateBatchSize($objectType, $crmIds);
$this->ensureValidToken();
try {
$batchConfig = $this->createBatchConfiguration($objectType);
$batchReadRequest = $this->prepareBatchRequest($batchConfig, $crmIds, $fields);
$response = $batchConfig['api']->read($batchReadRequest);
$this->validateApiResponse($response, $objectType);
$results = $this->processApiResults($response);
$this->logBatchResults($objectType, $crmIds, $results);
return $results;
} catch (\Throwable $e) {
$this->handleBatchError($e, $objectType, $crmIds);
}
}
private function validateBatchSize(string $objectType, array $crmIds): void
{
if (count($crmIds) > 100) {
throw new \InvalidArgumentException("Batch size cannot exceed 100 {$objectType}");
}
}
private function createBatchConfiguration(string $objectType): array
{
$configurations = [
'deals' => [
'batchReadRequest' => new \HubSpot\Client\Crm\Deals\Model\BatchReadInputSimplePublicObjectId(),
'inputClass' => \HubSpot\Client\Crm\Deals\Model\SimplePublicObjectId::class,
'api' => $this->getNewInstance()->crm()->deals()->batchApi(),
],
'companies' => [
'batchReadRequest' => new \HubSpot\Client\Crm\Companies\Model\BatchReadInputSimplePublicObjectId(),
'inputClass' => \HubSpot\Client\Crm\Companies\Model\SimplePublicObjectId::class,
'api' => $this->getNewInstance()->crm()->companies()->batchApi(),
],
'contacts' => [
'batchReadRequest' => new \HubSpot\Client\Crm\Contacts\Model\BatchReadInputSimplePublicObjectId(),
'inputClass' => \HubSpot\Client\Crm\Contacts\Model\SimplePublicObjectId::class,
'api' => $this->getNewInstance()->crm()->contacts()->batchApi(),
],
];
if (! isset($configurations[$objectType])) {
throw new \InvalidArgumentException("Unsupported object type: {$objectType}");
}
return $configurations[$objectType];
}
private function prepareBatchRequest(array $batchConfig, array $crmIds, array $fields): object
{
$batchReadRequest = $batchConfig['batchReadRequest'];
$inputClass = $batchConfig['inputClass'];
$inputs = array_map(function ($crmId) use ($inputClass) {
$input = new $inputClass();
$input->setId($crmId);
return $input;
}, $crmIds);
$batchReadRequest->setInputs($inputs);
$batchReadRequest->setProperties($fields);
return $batchReadRequest;
}
private function validateApiResponse($response, string $objectType): void
{
if (! $response) {
throw new CrmException("HubSpot API returned null response for {$objectType} batch read");
}
}
private function processApiResults($response): array
{
$results = [];
$responseResults = $response->getResults();
if ($responseResults) {
foreach ($responseResults as $object) {
if ($object && $object->getId()) {
$results[$object->getId()] = [
'id' => $object->getId(),
'properties' => $object->getProperties() ?: [],
];
}
}
}
return $results;
}
private function logBatchResults(string $objectType, array $crmIds, array $results): void
{
$this->log->info("[HubSpot] Batch fetched {$objectType}", [
'requested_count' => count($crmIds),
'returned_count' => count($results),
'crm_ids' => $crmIds,
]);
}
private function handleBatchError(\Throwable $e, string $objectType, array $crmIds): void
{
$errorMessage = $e->getMessage() ?: 'Unknown error';
$errorTrace = $e->getTraceAsString() ?: 'No trace available';
$this->log->error("[HubSpot] Failed to batch fetch {$objectType}", [
'crm_ids' => $crmIds,
'error' => $errorMessage,
'trace' => $errorTrace,
]);
throw new CrmException("Failed to batch fetch {$objectType}: " . $errorMessage);
}
/**
* Batch read multiple opportunities by their CRM IDs
*
* @param array<string> $crmIds Array of HubSpot deal IDs (max 100)
* @param array<string> $fields Array of property names to fetch
*
* @return array<string, array> Array keyed by CRM ID with opportunity data
*/
public function getOpportunitiesByIds(array $crmIds, array $fields): array
{
return $this->batchReadObjects('deals', $crmIds, $fields);
}
/**
* Batch read multiple companies by their CRM IDs
*
* @param array<string> $crmIds Array of HubSpot company IDs (max 100)
* @param array<string> $fields Array of property names to fetch
*
* @return array<string, array> Array keyed by CRM ID with company data
*/
public function getCompaniesByIds(array $crmIds, array $fields): array
{
return $this->batchReadObjects('companies', $crmIds, $fields);
}
/**
* Batch read multiple contacts by their CRM IDs
*
* @param array<string> $crmIds Array of HubSpot contact IDs (max 100)
* @param array<string> $fields Array of property names to fetch
*
* @return array<string, array> Array keyed by CRM ID with contact data
*/
public function getContactsByIds(array $crmIds, array $fields): array
{
return $this->batchReadObjects('contacts', $crmIds, $fields);
}
/**
* @throws CompanyApiException
* @throws CrmException
*/
public function getAccountById(string $crmId, array $fields): array
{
try {
$company = $this->getNewInstance()->crm()->companies()->basicApi()->getById(
$crmId,
implode(',', $fields),
);
} catch (CompanyApiException $e) {
$this->log->info('[Hubspot] Failed to fetch account', [
'crm_id' => $crmId,
'reason' => $e->getMessage(),
]);
throw $e;
}
if (! $company instanceof CompaniesWithAssociations) {
throw new CrmException('Account not found');
}
return [
'id' => $company->getId(),
'properties' => $company->getProperties(),
];
}
/**
* @throws ContactApiException
* @throws CrmException
*/
public function getContactById(string $crmId, array $fields): array
{
try {
$contact = $this->getNewInstance()->crm()->contacts()->basicApi()->getById(
$crmId,
implode(',', $fields)
);
} catch (ContactApiException $e) {
$this->log->info('[Hubspot] Failed to fetch contact', [
'crm_id' => $crmId,
'reason' => $e->getMessage(),
]);
throw $e;
}
if (! $contact instanceof ContactsWithAssociations) {
throw new CrmException('Contact not found');
}
return [
'id' => $contact->getId(),
'properties' => $contact->getProperties(),
];
}
/**
* This is email search request that Hubspot offers as GET (more generous quota)
*/
public function getContactByEmail(string $email, array $fields = []): array
{
try {
$contact = $this->getNewInstance()->crm()->contacts()->basicApi()->getById(
$email,
implode(',', $fields),
null,
false,
'email'
);
return [
'id' => $contact->getId(),
'properties' => $contact->getProperties(),
];
} catch (ContactApiException $e) {
$this->log->info('[Hubspot] Failed to fetch contact', [
'email' => $email,
'reason' => $e->getMessage(),
]);
return [];
}
}
/**
* @throws CrmException
*/
public function fetchProperty(string $objectType, string $propertyId): Property
{
$result = $this->getNewInstance()->crm()->properties()->coreApi()->getByName($objectType, $propertyId);
if (! $result instanceof Property) {
$this->log->error('[Hubspot] Failed to fetch property', [
'object_type' => $objectType,
'property_id' => $propertyId,
'reason' => $result->getMessage(),
]);
throw new CrmException('Failed to fetch property');
}
return $result;
}
/**
* @return array<CrmFieldOption>
*/
public function fetchPropertyOptions(string $objectType, string $propertyId): array
{
/** @var array<CrmFieldOption> */
return $this->fetchProperty($objectType, $propertyId)->getOptions();
}
/**
* @return array<array{id:string, label:string, deleted:bool}>
*/
public function fetchCallDispositions(): array
{
/** @var Response $response */
$response = $this->getInstance()->engagements()->getCallDispositions();
/**
* @var array<array{
* id:string,
* label:string,
* deleted: bool
* }>
*/
return $response->toArray();
}
/**
* @return array<CrmFieldOption>
*/
public function fetchOpportunityPipelineStages(): array
{
$stages = [];
$apiResponse = $this->getNewInstance()->crm()->pipelines()->pipelinesApi()->getAll('deals');
if ($apiResponse instanceof Error) {
$this->log->error('[Hubspot] Failed to fetch opportunity pipelines', [
'reason' => $apiResponse->getMessage(),
]);
return [];
}
foreach ($apiResponse->getResults() as $pipeline) {
$pipelineStages = array_map(
static function (PipelineStage $stage) {
return [
'id' => $stage->getId(),
'label' => $stage->getLabel(),
];
},
$pipeline->getStages()
);
$stages = array_merge($stages, $pipelineStages);
}
return $stages;
}
public function fetchOpportunityPipelines(): array
{
$pipelines = [];
try {
$apiResponse = $this->makeRequest('/crm/v3/pipelines/deals');
} catch (\Exception $e) {
$this->log->info('[Hubspot] Failed to fetch opportunity pipelines', [
'reason' => $e->getMessage(),
]);
return [];
}
$response = $apiResponse->toArray();
foreach ($response['results'] as $pipeline) {
$pipelines[] = [
'id' => $pipeline['id'],
'label' => $pipeline['label'],
];
}
return $pipelines;
}
/**
* @return array<CrmFieldOption>
*/
public function fetchMeetingOutcomeFieldOptions(Field $field): array
{
return $field->getCrmProviderId() === 'meetingOutcome'
? $this->fetchMeetingOutcomeTypes()
: $this->fetchCallActivityTypes();
}
public function fetchMeetingOutcomeTypes(): array
{
return $this->extractMeetingTypeOptions(
'[URL_WITH_CREDENTIALS] Response $response */
$response = $this->getInstance()
->getClient()
->request('GET', $endpoint);
/**
* @var array<array{
* value: string,
* label: string,
* displayOrder: int
* }> $optionData
*/
$optionData = $response->toArray()['options'] ?? [];
$options = [];
foreach ($optionData as $item) {
$options[] = [
'id' => $item['value'],
'value' => $item['value'],
'label' => $item['label'],
'display_order' => $item['displayOrder'],
];
}
return $options;
}
/**
* @return array<CrmFieldOption>
*/
public function fetchDispositionFieldOptions(): array
{
$options = [];
$dispositions = $this->fetchCallDispositions();
foreach ($dispositions as $disposition) {
if ($disposition['deleted'] !== false) {
continue;
}
$option['value'] = $disposition['id'];
$option['id'] = $disposition['id'];
$option['label'] = $disposition['label'];
$options[] = $option;
}
return $options;
}
/**
* @return array<CrmFieldOption>
*/
public function fetchOpportunityFieldOptions(Field $field): array
{
if ($field->isStageField()) {
return $this->fetchOpportunityPipelineStages();
}
if ($field->isPipelineField()) {
return $this->fetchOpportunityPipelines();
}
return $this->fetchPropertyOptions('deals', $field->getCrmProviderId());
}
/**
* @throws BadRequest
* @throws HubspotException
*/
public function makeRequest(string $endpoint, $method = 'GET', $payload = [], ?string $queryString = null)
{
$endpoint = self::BASE_URL . $endpoint;
if ($method === 'GET') {
return $this->getInstance()->getClient()?->request(
method: $method,
endpoint: $endpoint,
query_string: $queryString
);
} else {
return $this->getInstance()->getClient()->request($method, $endpoint, [
'json' => ($payload),
]);
}
}
/**
* @throws BadRequest
* @throws HubspotException
*/
public function createMeeting(array $payload): Response
{
$endpoint = '/crm/v3/objects/meetings';
return $this->makeRequest($endpoint, 'POST', $payload);
}
/**
* @throws BadRequest
* @throws HubspotException
*/
public function updateMeeting(string $meetingId, array $payload): Response
{
$endpoint = '/crm/v3/objects/meetings/' . $meetingId;
return $this->makeRequest($endpoint, 'PATCH', $payload);
}
/**
* @throws \Exception
*/
public function createNote(
string $body,
string $ownerId,
int $timestamp,
string $objectId,
NoteObject $noteObject
): ?string {
try {
$noteInput = new SimplePublicObjectInput([
'properties' => [
'hs_note_body' => $body,
'hubspot_owner_id' => $ownerId,
'hs_timestamp' => $timestamp,
],
]);
// Create note
$note = $this->getNewInstance()->crm()->objects()->basicApi()->create('note', $noteInput);
$this->getNewInstance()->crm()->objects()->associationsApi()->create(
'note',
$note->getId(),
$this->getNoteObject($noteObject),
$objectId,
$this->getNoteAssociationType($noteObject),
);
return $note->getId();
} catch (\Exception $e) {
$this->log->error('[Hubspot] Failed to create note', [
'objectId' => $objectId,
'noteObject' => $noteObject->getObjectType(),
'reason' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
return null;
}
public function updateEngagement(string $objectId, array $engagement, array $metadata): void
{
$this->getInstance()->engagements()->update($objectId, $engagement, $metadata);
}
public function getEngagementData(string $engagementId): array
{
$engagement = $this->getInstance()->engagements()->get($engagementId);
return $engagement->toArray();
}
public function createEngagement(array $engagement, array $associations, array $metadata): Response
{
return $this->getInstance()
->engagements()
->create($engagement, $associations, $metadata);
}
public function isUnauthorizedException(\Exception $e): bool
{
// Check for specific HubSpot API exception types first
if ($e instanceof BadRequest) {
// BadRequest can contain 401 status codes
return $e->getCode() === 401;
}
// Check for HTTP client exceptions with status codes
if ($e instanceof \GuzzleHttp\Exception\RequestException && $e->hasResponse()) {
$response = $e->getResponse();
if ($response !== null) {
return $response->getStatusCode() === 401;
}
}
// Check for Guzzle HTTP exceptions
if ($e instanceof \GuzzleHttp\Exception\ClientException) {
return $e->getCode() === 401;
}
// Fallback to string matching as last resort, but be more specific
$message = strtolower($e->getMessage());
return str_contains($message, '401 unauthorized') ||
str_contains($message, 'http 401') ||
str_contains($message, 'status code 401') ||
(preg_match('/\b401\b/', $message) && str_contains($message, 'unauthorized'));
}
/**
* Validates and refreshes the access token if needed before API requests.
* This ensures long-running processes don't fail due to token expiration.
*
* @throws SocialAccountTokenInvalidException
*/
public function ensureValidToken(): void
{
if ($this->oauthAccount === null) {
return;
}
$newToken = $this->tokenManager->ensureValidToken($this->oauthAccount);
if ($newToken !== null) {
$this->accessToken = $newToken;
}
}
public function getConfig()
{
return $this->config;
}
// returns only active (archived=false)
public function getOwners(): array
{
return $this->getNewInstance()->crm()->owners()->getAll();
}
/**
* @param bool $archived
*
* @return array<Owner>|[]
*/
public function getOwnersArchived(bool $archived = true): array
{
$endpoint = '/crm/v3/owners';
$queryParams = [
'archived' => $archived ? 'true' : 'false',
];
$queryString = http_build_query($queryParams);
$owners = [];
try {
$response = $this->makeRequest(endpoint: $endpoint, queryString: $queryString);
$responseData = $response?->toArray();
foreach ($responseData['results'] as $result) {
try {
$owners[] = Owner::create($result);
} catch (Throwable $e) {
$this->log->error('[HubSpot] Failed to process owner data', [
'result' => $result,
'error' => $e->getMessage(),
]);
continue;
}
}
} catch (Throwable $e) {
$this->log->error('HubSpot] Failed to fetch owners', [
'archived' => $archived,
'error' => $e->getMessage(),
]);
return [];
}
return $owners;
}
public function getMeeting(string $engagementId): ObjectWithAssociations
{
return $this->getNewInstance()->crm()->objects()->basicApi()
->getById('meeting', $engagementId, null, 'contact,company,deal');
}
public function deleteEngagement(string $engagementId): void
{
$this->getInstance()->engagements()->delete((int) $engagementId);
}
public function getAssociationsData(array $ids, string $fromObject, string $toObject): array
{
$associationData = [];
$idChunks = array_chunk($ids, self::ASSOCIATIONS_BATCH_SIZE_LIMIT);
foreach ($idChunks as $idChunk) {
try {
$batchInput = new \HubSpot\Client\Crm\Associations\Model\BatchInputPublicObjectId();
$batchInput->setInputs(array_map(function ($id) {
$publicObjectId = new \HubSpot\Client\Crm\Associations\Model\PublicObjectId();
$publicObjectId->setId($id);
return $publicObjectId;
}, $idChunk));
$associatedObjectsData = $this
->getNewInstance()
->crm()
->associations()
->batchApi()
->read($fromObject, $toObject, $batchInput);
if ($associatedObjectsData instanceof \HubSpot\Client\Crm\Associations\Model\BatchResponsePublicAssociationMulti) {
foreach ($associatedObjectsData->getResults() as $association) {
$from = $association->getFrom()->getId();
$toAssociations = $association->getTo();
if (! empty($toAssociations)) {
$associationData[$from] = array_map(function ($item) {
return $item->getId();
}, $toAssociations);
}
}
}
// } catch (RateLimitException $e) {
// throw $e;
} catch (\Exception $e) {
$this->log->error('[Hubspot] Failed to fetch associations', [
'from_object' => $fromObject,
'to_object' => $toObject,
'reason' => $e->getMessage(),
]);
}
}
return $associationData;
}
/**
* @throws \Exception
*/
private function getNoteAssociationType(NoteObject $noteObject): string
{
return match($noteObject) {
NoteObject::Opportunity => 'note_to_deal',
NoteObject::Lead, NoteObject::Contact => 'note_to_contact', // or 'note_to_lead' if your portal supports it
NoteObject::Account => 'note_to_company',
NoteObject::Call, NoteObject::Event => throw new \Exception('Not supported'),
};
}
/**
* @throws \Exception
*/
private function getNoteObject(NoteObject $noteObject): string
{
return match($noteObject) {
NoteObject::Opportunity => 'deal',
NoteObject::Lead, NoteObject::Contact => 'contact',
NoteObject::Account => 'company',
NoteObject::Call, NoteObject::Event => throw new \Exception('Not supported'),
};
}
public function addAssociations(string $objectType, string $associationType, array $payload): Response
{
$endpoint = "/crm/v4/associations/$objectType/$associationType/batch/create";
return $this->makeRequest($endpoint, 'POST', $payload);
}
public function removeAssociations(string $objectType, string $associationType, array $payload): Response
{
$endpoint = "/crm/v4/associations/$objectType/$associationType/batch/archive";
return $this->makeRequest($endpoint, 'POST', $payload);
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
19
Previous Highlighted Error
Next Highlighted Error
[2026-05-07 14:21:15] local.INFO: [Hubspot] DEBUG Getting headers {
"headers":{
"Date":["Thu,07 May 2026 14:21:15 GMT"],
"Content-Type":["application/json;charset=utf-8"],
"Transfer-Encoding":["chunked"],
"Connection":["keep-alive"],
"CF-Ray":["9f80deb8db60dc3a-SOF"],
"CF-Cache-Status":["DYNAMIC"],
"Strict-Transport-Security":["max-age=31536000; includeSubDomains; preload"],
"Vary":["origin,
accept-encoding"],
"access-control-allow-credentials":["false"],
"server-timing":["hcid;desc=\"019e02d0-6fd8-7812-bdba-885b7ccb3ee3\",
cfr;desc=\"9f80deb8e7c6dc3a-IAD\""],
"x-content-type-options":["nosniff"],
"x-hubspot-correlation-id":["019e02d0-6fd8-7812-bdba-885b7ccb3ee3"],
"Set-Cookie":["__cf_bm=SIUrtdQgXVrik50pdqF6hZVYKhzTnQBidvMabeCtm0Y-1778163675-[IP_ADDRESS]-rI.ZggtDKxTge5zr8_2gbBfWMQQ.ufZEXDZyHz2mBUFdzdo2gTHEsOkXMSEShjK0hGYxNhUGM1ZoBpX7BcFZcHEjA7Cs_.SMUhUnd2nYjko; path=/; expires=Thu,
07-May-26 14:51:15 GMT; domain=.hubapi.com; HttpOnly; Secure; SameSite=None"],
"Report-To":["{
\"endpoints\":[{
\"url\":\"https:\\/\\/a.nel.cloudflare.com\\/report\\/v4?s=NYAlsVTP0fYm32qrSDjxYE4sd2RWRqiSp3wHsmdEgZlzoYdxI%2BIxVpHmsKn3O%2BKVA3mFIJ2m7YRECDGSM%2BW2IYTzo6FM4%2BdUIjURO8srzKSvJgZ%2BQ6R79arKQw3uHLlX\"}],
\"group\":\"cf-nel\",
\"max_age\":604800}"],
"NEL":["{
\"success_fraction\":0.01,
\"report_to\":\"cf-nel\",
\"max_age\":604800}"],
"Server":["cloudflare"]}} {
"correlation_id":"95236535-ec98-4541-b92a-adfa73b69eab",
"trace_id":"c7ab8365-903f-46d4-9403-0e5b551e3545"}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
18195
|
NULL
|
NULL
|
NULL
|
|
18198
|
791
|
4
|
2026-05-11T11:03:48.083195+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-11/1778 /Users/lukas/.screenpipe/data/data/2026-05-11/1778497428083_m2.jpg...
|
PhpStorm
|
faVsco.js – Client.php
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
JY-20725-handle-HS-search Project: faVsco.js, menu
JY-20725-handle-HS-search-rate-limit, menu
Start Listening for PHP Debug Connections
HandleHubspotRateLimitTest
Run 'HandleHubspotRateLimitTest'
Debug 'HandleHubspotRateLimitTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
2
67
3
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Services\Crm\Hubspot;
use HubSpot\Client\Crm\Deals\ApiException as DealApiException;
use HubSpot\Client\Crm\Contacts\ApiException as ContactApiException;
use HubSpot\Client\Crm\Companies\ApiException as CompanyApiException;
use HubSpot\Client\Crm\Contacts\Model\SimplePublicObjectWithAssociations as ContactsWithAssociations;
use HubSpot\Client\Crm\Companies\Model\SimplePublicObjectWithAssociations as CompaniesWithAssociations;
use HubSpot\Client\Crm\Deals\Model\SimplePublicObjectWithAssociations as DealWithAssociations;
use HubSpot\Client\Crm\Objects\Model\SimplePublicObjectInput;
use HubSpot\Client\Crm\Objects\Model\SimplePublicObjectWithAssociations as ObjectWithAssociations;
use HubSpot\Client\Crm\Pipelines\Model\Error;
use HubSpot\Client\Crm\Pipelines\Model\PipelineStage;
use HubSpot\Client\Crm\Properties\Model\Property;
use HubSpot\Discovery\Discovery;
use Jiminny\Exceptions\CrmException;
use Jiminny\Exceptions\RateLimitException;
use Jiminny\Exceptions\SocialAccountTokenInvalidException;
use Jiminny\Jobs\Crm\NoteObject;
use Jiminny\Models\Crm\Field;
use Jiminny\Services\Crm\BaseClient;
use Jiminny\Services\Crm\Hubspot\DTO\Response\Owner;
use Jiminny\Services\SocialAccountService;
use SevenShores\Hubspot\Exceptions\BadRequest;
use SevenShores\Hubspot\Exceptions\HubspotException;
use SevenShores\Hubspot\Factory;
use SevenShores\Hubspot\Http\Response;
use Jiminny\Services\Crm\Hubspot\Pagination\HubspotPaginationService;
use Illuminate\Support\Facades\Redis;
use Throwable;
/**
* @phpstan-type CrmFieldOption array{id:string, label:string, value?:string}
*/
class Client extends BaseClient implements HubspotClientInterface
{
public const string MIN_API_VERSION = '2';
public const string BASE_URL = '[URL_WITH_CREDENTIALS] T
* @param callable(): T $apiCall
* @return T
*
* @throws RateLimitException
*/
private function executeRequest(callable $apiCall)
{
$cacheKey = $this->getRateLimitCacheKey();
$cachedRetryAfter = Redis::get($cacheKey);
if (is_string($cachedRetryAfter) && is_numeric($cachedRetryAfter)) {
throw new RateLimitException(
'Hubspot rate limit (cached circuit-breaker)',
(int) $cachedRetryAfter,
);
}
try {
return $apiCall();
} catch (Throwable $e) {
if ($this->isHubspotRateLimit($e)) {
$retryAfter = $this->parseRetryAfter($e);
Redis::setex($cacheKey, $retryAfter, (string) $retryAfter);
$this->log->warning('[Hubspot] Received 429 from API', [
'team_id' => $this->config->team_id,
'config_id' => $this->config->getId(),
'retry_after' => $retryAfter,
'message' => $this->parseMessage($e),
'reason' => $e->getMessage(),
]);
throw new RateLimitException('Hubspot returned 429', $retryAfter, $e);
}
throw $e;
}
}
private function getRateLimitCacheKey(): string
{
return sprintf('hubspot:ratelimit:portal:%d', $this->config->getId());
}
public function isHubspotRateLimit(Throwable $e): bool
{
if ($e instanceof BadRequest
|| $e instanceof DealApiException
|| $e instanceof ContactApiException
|| $e instanceof CompanyApiException
|| $e instanceof \GuzzleHttp\Exception\RequestException
) {
return (int) $e->getCode() === 429;
}
return false;
}
public function parseRetryAfter(Throwable $e): int
{
if (method_exists($e, 'getResponseHeaders')) {
$headers = $e->getResponseHeaders() ?: [];
$value = $headers['Retry-After'] ?? $headers['retry-after'] ?? null;
if (is_array($value)) {
$value = $value[0] ?? null;
}
if (is_numeric($value)) {
return (int) $value;
}
}
$message = strtolower($this->parseMessage($e));
if (str_contains($message, 'daily')) {
return 600;
}
if (str_contains($message, 'ten secondly')) {
return 10;
}
if (str_contains($message, 'secondly')) {
return 1;
}
$this->log->warning('[Hubspot] No retry-after header or known message, using default', [
'exception_class' => get_class($e),
'message' => $message,
]);
return 10;
}
public function parseMessage(Throwable $e): string
{
if (! method_exists($e, 'getResponseBody')) {
return '';
}
$body = $e->getResponseBody();
if (is_string($body)) {
$body = json_decode($body, true) ?? [];
}
if (! is_array($body)) {
return '';
}
return (string) ($body['message'] ?? '');
}
public function getMinimumApiVersion(): string
{
return self::MIN_API_VERSION;
}
public function getInstance(): Factory
{
return new Factory([
'key' => $this->accessToken,
'oauth2' => true,
'base_url' => $this->baseUrl,
]);
}
public function getNewInstance(): Discovery
{
return \HubSpot\Factory::createWithAccessToken($this->accessToken);
}
/**
* Secondly and daily limits for Hubspot API
*
* Product Tier: Free & Starter | Professional & Enterprise | API add-on (any tier)
* Burst: 100/10 seconds | 150/10 seconds | 200/10 seconds
* Daily: 250,000 | 500,000 | 1,000,000
*
* Official documentation states: The search endpoints are rate limited to five requests per second.
* Since with 5 RPS were still hitting secondly rate limits we lowered it to 4
*/
public function getPaginatedData(array $payload, string $type, int $offset = 0): array
{
$total = 0;
$lastId = null;
$rows = [];
foreach ($this->getPaginatedDataGenerator($payload, $type, $offset, $total, $lastId) as $row) {
$rows[] = $row;
}
return ['results' => $rows, 'total' => $total, 'last_record' => $lastId];
}
/**
* @throws HubspotException
* @throws SocialAccountTokenInvalidException
* @throws BadRequest
*/
public function getPaginatedDataGenerator(
array $payload,
string $type,
int $offset = 0,
int &$total = 0,
?string &$lastRecordId = null
): \Generator {
return $this->paginationService->getPaginatedDataGenerator(
$this,
$payload,
$type,
$offset,
$total,
$lastRecordId
);
}
/**
* Execute a search request against HubSpot CRM objects with rate limiting.
*
* @param string $objectType The object type ('deals', 'companies', 'contacts', 'calls')
* @param array<string, mixed> $payload The search payload with filters, sorts, properties, etc.
* @return array The search response with 'results', 'total', 'paging' keys
* @throws RateLimitException When rate limit is hit
* @throws HubspotException On API errors
*/
public function search(string $objectType, array $payload): array
{
$endpoint = self::BASE_URL . "/crm/v3/objects/{$objectType}/search";
return $this->executeRequest(function () use ($endpoint, $payload) {
$response = $this->getInstance()->getClient()->request('POST', $endpoint, ['json' => $payload]);
return $response->toArray();
});
}
/**
* @throws DealApiException
* @throws CrmException
*/
public function getOpportunityById(string $crmId, array $fields): array
{
try {
// $deal = $this->executeRequest(fn () => $this->getNewInstance()->crm()->deals()->basicApi()->getById(
$deal = $this->getNewInstance()->crm()->deals()->basicApi()->getById(
$crmId,
implode(',', $fields),
'companies,contacts'
);
} catch (DealApiException $e) {
$this->log->info('[Hubspot] Failed to fetch opportunity', [
'crm_id' => $crmId,
'reason' => $e->getMessage(),
]);
throw $e;
}
if (! $deal instanceof DealWithAssociations) {
throw new CrmException('Deal not found');
}
return [
'id' => $deal->getId(),
'properties' => $deal->getProperties(),
'associations' => $deal->getAssociations(),
];
}
/**
* Generic batch read method for HubSpot objects
*
* @param string $objectType The object type ('deals', 'companies', 'contacts')
* @param array<string> $crmIds Array of HubSpot object IDs (max 100)
* @param array<string> $fields Array of property names to fetch
*
* @return array<string, array> Array keyed by CRM ID with object data
*/
private function batchReadObjects(string $objectType, array $crmIds, array $fields): array
{
if (empty($crmIds)) {
return [];
}
$this->validateBatchSize($objectType, $crmIds);
$this->ensureValidToken();
try {
$batchConfig = $this->createBatchConfiguration($objectType);
$batchReadRequest = $this->prepareBatchRequest($batchConfig, $crmIds, $fields);
$response = $batchConfig['api']->read($batchReadRequest);
$this->validateApiResponse($response, $objectType);
$results = $this->processApiResults($response);
$this->logBatchResults($objectType, $crmIds, $results);
return $results;
} catch (\Throwable $e) {
$this->handleBatchError($e, $objectType, $crmIds);
}
}
private function validateBatchSize(string $objectType, array $crmIds): void
{
if (count($crmIds) > 100) {
throw new \InvalidArgumentException("Batch size cannot exceed 100 {$objectType}");
}
}
private function createBatchConfiguration(string $objectType): array
{
$configurations = [
'deals' => [
'batchReadRequest' => new \HubSpot\Client\Crm\Deals\Model\BatchReadInputSimplePublicObjectId(),
'inputClass' => \HubSpot\Client\Crm\Deals\Model\SimplePublicObjectId::class,
'api' => $this->getNewInstance()->crm()->deals()->batchApi(),
],
'companies' => [
'batchReadRequest' => new \HubSpot\Client\Crm\Companies\Model\BatchReadInputSimplePublicObjectId(),
'inputClass' => \HubSpot\Client\Crm\Companies\Model\SimplePublicObjectId::class,
'api' => $this->getNewInstance()->crm()->companies()->batchApi(),
],
'contacts' => [
'batchReadRequest' => new \HubSpot\Client\Crm\Contacts\Model\BatchReadInputSimplePublicObjectId(),
'inputClass' => \HubSpot\Client\Crm\Contacts\Model\SimplePublicObjectId::class,
'api' => $this->getNewInstance()->crm()->contacts()->batchApi(),
],
];
if (! isset($configurations[$objectType])) {
throw new \InvalidArgumentException("Unsupported object type: {$objectType}");
}
return $configurations[$objectType];
}
private function prepareBatchRequest(array $batchConfig, array $crmIds, array $fields): object
{
$batchReadRequest = $batchConfig['batchReadRequest'];
$inputClass = $batchConfig['inputClass'];
$inputs = array_map(function ($crmId) use ($inputClass) {
$input = new $inputClass();
$input->setId($crmId);
return $input;
}, $crmIds);
$batchReadRequest->setInputs($inputs);
$batchReadRequest->setProperties($fields);
return $batchReadRequest;
}
private function validateApiResponse($response, string $objectType): void
{
if (! $response) {
throw new CrmException("HubSpot API returned null response for {$objectType} batch read");
}
}
private function processApiResults($response): array
{
$results = [];
$responseResults = $response->getResults();
if ($responseResults) {
foreach ($responseResults as $object) {
if ($object && $object->getId()) {
$results[$object->getId()] = [
'id' => $object->getId(),
'properties' => $object->getProperties() ?: [],
];
}
}
}
return $results;
}
private function logBatchResults(string $objectType, array $crmIds, array $results): void
{
$this->log->info("[HubSpot] Batch fetched {$objectType}", [
'requested_count' => count($crmIds),
'returned_count' => count($results),
'crm_ids' => $crmIds,
]);
}
private function handleBatchError(\Throwable $e, string $objectType, array $crmIds): void
{
$errorMessage = $e->getMessage() ?: 'Unknown error';
$errorTrace = $e->getTraceAsString() ?: 'No trace available';
$this->log->error("[HubSpot] Failed to batch fetch {$objectType}", [
'crm_ids' => $crmIds,
'error' => $errorMessage,
'trace' => $errorTrace,
]);
throw new CrmException("Failed to batch fetch {$objectType}: " . $errorMessage);
}
/**
* Batch read multiple opportunities by their CRM IDs
*
* @param array<string> $crmIds Array of HubSpot deal IDs (max 100)
* @param array<string> $fields Array of property names to fetch
*
* @return array<string, array> Array keyed by CRM ID with opportunity data
*/
public function getOpportunitiesByIds(array $crmIds, array $fields): array
{
return $this->batchReadObjects('deals', $crmIds, $fields);
}
/**
* Batch read multiple companies by their CRM IDs
*
* @param array<string> $crmIds Array of HubSpot company IDs (max 100)
* @param array<string> $fields Array of property names to fetch
*
* @return array<string, array> Array keyed by CRM ID with company data
*/
public function getCompaniesByIds(array $crmIds, array $fields): array
{
return $this->batchReadObjects('companies', $crmIds, $fields);
}
/**
* Batch read multiple contacts by their CRM IDs
*
* @param array<string> $crmIds Array of HubSpot contact IDs (max 100)
* @param array<string> $fields Array of property names to fetch
*
* @return array<string, array> Array keyed by CRM ID with contact data
*/
public function getContactsByIds(array $crmIds, array $fields): array
{
return $this->batchReadObjects('contacts', $crmIds, $fields);
}
/**
* @throws CompanyApiException
* @throws CrmException
*/
public function getAccountById(string $crmId, array $fields): array
{
try {
$company = $this->getNewInstance()->crm()->companies()->basicApi()->getById(
$crmId,
implode(',', $fields),
);
} catch (CompanyApiException $e) {
$this->log->info('[Hubspot] Failed to fetch account', [
'crm_id' => $crmId,
'reason' => $e->getMessage(),
]);
throw $e;
}
if (! $company instanceof CompaniesWithAssociations) {
throw new CrmException('Account not found');
}
return [
'id' => $company->getId(),
'properties' => $company->getProperties(),
];
}
/**
* @throws ContactApiException
* @throws CrmException
*/
public function getContactById(string $crmId, array $fields): array
{
try {
$contact = $this->getNewInstance()->crm()->contacts()->basicApi()->getById(
$crmId,
implode(',', $fields)
);
} catch (ContactApiException $e) {
$this->log->info('[Hubspot] Failed to fetch contact', [
'crm_id' => $crmId,
'reason' => $e->getMessage(),
]);
throw $e;
}
if (! $contact instanceof ContactsWithAssociations) {
throw new CrmException('Contact not found');
}
return [
'id' => $contact->getId(),
'properties' => $contact->getProperties(),
];
}
/**
* This is email search request that Hubspot offers as GET (more generous quota)
*/
public function getContactByEmail(string $email, array $fields = []): array
{
try {
$contact = $this->getNewInstance()->crm()->contacts()->basicApi()->getById(
$email,
implode(',', $fields),
null,
false,
'email'
);
return [
'id' => $contact->getId(),
'properties' => $contact->getProperties(),
];
} catch (ContactApiException $e) {
$this->log->info('[Hubspot] Failed to fetch contact', [
'email' => $email,
'reason' => $e->getMessage(),
]);
return [];
}
}
/**
* @throws CrmException
*/
public function fetchProperty(string $objectType, string $propertyId): Property
{
$result = $this->getNewInstance()->crm()->properties()->coreApi()->getByName($objectType, $propertyId);
if (! $result instanceof Property) {
$this->log->error('[Hubspot] Failed to fetch property', [
'object_type' => $objectType,
'property_id' => $propertyId,
'reason' => $result->getMessage(),
]);
throw new CrmException('Failed to fetch property');
}
return $result;
}
/**
* @return array<CrmFieldOption>
*/
public function fetchPropertyOptions(string $objectType, string $propertyId): array
{
/** @var array<CrmFieldOption> */
return $this->fetchProperty($objectType, $propertyId)->getOptions();
}
/**
* @return array<array{id:string, label:string, deleted:bool}>
*/
public function fetchCallDispositions(): array
{
/** @var Response $response */
$response = $this->getInstance()->engagements()->getCallDispositions();
/**
* @var array<array{
* id:string,
* label:string,
* deleted: bool
* }>
*/
return $response->toArray();
}
/**
* @return array<CrmFieldOption>
*/
public function fetchOpportunityPipelineStages(): array
{
$stages = [];
$apiResponse = $this->getNewInstance()->crm()->pipelines()->pipelinesApi()->getAll('deals');
if ($apiResponse instanceof Error) {
$this->log->error('[Hubspot] Failed to fetch opportunity pipelines', [
'reason' => $apiResponse->getMessage(),
]);
return [];
}
foreach ($apiResponse->getResults() as $pipeline) {
$pipelineStages = array_map(
static function (PipelineStage $stage) {
return [
'id' => $stage->getId(),
'label' => $stage->getLabel(),
];
},
$pipeline->getStages()
);
$stages = array_merge($stages, $pipelineStages);
}
return $stages;
}
public function fetchOpportunityPipelines(): array
{
$pipelines = [];
try {
$apiResponse = $this->makeRequest('/crm/v3/pipelines/deals');
} catch (\Exception $e) {
$this->log->info('[Hubspot] Failed to fetch opportunity pipelines', [
'reason' => $e->getMessage(),
]);
return [];
}
$response = $apiResponse->toArray();
foreach ($response['results'] as $pipeline) {
$pipelines[] = [
'id' => $pipeline['id'],
'label' => $pipeline['label'],
];
}
return $pipelines;
}
/**
* @return array<CrmFieldOption>
*/
public function fetchMeetingOutcomeFieldOptions(Field $field): array
{
return $field->getCrmProviderId() === 'meetingOutcome'
? $this->fetchMeetingOutcomeTypes()
: $this->fetchCallActivityTypes();
}
public function fetchMeetingOutcomeTypes(): array
{
return $this->extractMeetingTypeOptions(
'[URL_WITH_CREDENTIALS] Response $response */
$response = $this->getInstance()
->getClient()
->request('GET', $endpoint);
/**
* @var array<array{
* value: string,
* label: string,
* displayOrder: int
* }> $optionData
*/
$optionData = $response->toArray()['options'] ?? [];
$options = [];
foreach ($optionData as $item) {
$options[] = [
'id' => $item['value'],
'value' => $item['value'],
'label' => $item['label'],
'display_order' => $item['displayOrder'],
];
}
return $options;
}
/**
* @return array<CrmFieldOption>
*/
public function fetchDispositionFieldOptions(): array
{
$options = [];
$dispositions = $this->fetchCallDispositions();
foreach ($dispositions as $disposition) {
if ($disposition['deleted'] !== false) {
continue;
}
$option['value'] = $disposition['id'];
$option['id'] = $disposition['id'];
$option['label'] = $disposition['label'];
$options[] = $option;
}
return $options;
}
/**
* @return array<CrmFieldOption>
*/
public function fetchOpportunityFieldOptions(Field $field): array
{
if ($field->isStageField()) {
return $this->fetchOpportunityPipelineStages();
}
if ($field->isPipelineField()) {
return $this->fetchOpportunityPipelines();
}
return $this->fetchPropertyOptions('deals', $field->getCrmProviderId());
}
/**
* @throws BadRequest
* @throws HubspotException
*/
public function makeRequest(string $endpoint, $method = 'GET', $payload = [], ?string $queryString = null)
{
$endpoint = self::BASE_URL . $endpoint;
if ($method === 'GET') {
return $this->getInstance()->getClient()?->request(
method: $method,
endpoint: $endpoint,
query_string: $queryString
);
} else {
return $this->getInstance()->getClient()->request($method, $endpoint, [
'json' => ($payload),
]);
}
}
/**
* @throws BadRequest
* @throws HubspotException
*/
public function createMeeting(array $payload): Response
{
$endpoint = '/crm/v3/objects/meetings';
return $this->makeRequest($endpoint, 'POST', $payload);
}
/**
* @throws BadRequest
* @throws HubspotException
*/
public function updateMeeting(string $meetingId, array $payload): Response
{
$endpoint = '/crm/v3/objects/meetings/' . $meetingId;
return $this->makeRequest($endpoint, 'PATCH', $payload);
}
/**
* @throws \Exception
*/
public function createNote(
string $body,
string $ownerId,
int $timestamp,
string $objectId,
NoteObject $noteObject
): ?string {
try {
$noteInput = new SimplePublicObjectInput([
'properties' => [
'hs_note_body' => $body,
'hubspot_owner_id' => $ownerId,
'hs_timestamp' => $timestamp,
],
]);
// Create note
$note = $this->getNewInstance()->crm()->objects()->basicApi()->create('note', $noteInput);
$this->getNewInstance()->crm()->objects()->associationsApi()->create(
'note',
$note->getId(),
$this->getNoteObject($noteObject),
$objectId,
$this->getNoteAssociationType($noteObject),
);
return $note->getId();
} catch (\Exception $e) {
$this->log->error('[Hubspot] Failed to create note', [
'objectId' => $objectId,
'noteObject' => $noteObject->getObjectType(),
'reason' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
return null;
}
public function updateEngagement(string $objectId, array $engagement, array $metadata): void
{
$this->getInstance()->engagements()->update($objectId, $engagement, $metadata);
}
public function getEngagementData(string $engagementId): array
{
$engagement = $this->getInstance()->engagements()->get($engagementId);
return $engagement->toArray();
}
public function createEngagement(array $engagement, array $associations, array $metadata): Response
{
return $this->getInstance()
->engagements()
->create($engagement, $associations, $metadata);
}
public function isUnauthorizedException(\Exception $e): bool
{
// Check for specific HubSpot API exception types first
if ($e instanceof BadRequest) {
// BadRequest can contain 401 status codes
return $e->getCode() === 401;
}
// Check for HTTP client exceptions with status codes
if ($e instanceof \GuzzleHttp\Exception\RequestException && $e->hasResponse()) {
$response = $e->getResponse();
if ($response !== null) {
return $response->getStatusCode() === 401;
}
}
// Check for Guzzle HTTP exceptions
if ($e instanceof \GuzzleHttp\Exception\ClientException) {
return $e->getCode() === 401;
}
// Fallback to string matching as last resort, but be more specific
$message = strtolower($e->getMessage());
return str_contains($message, '401 unauthorized') ||
str_contains($message, 'http 401') ||
str_contains($message, 'status code 401') ||
(preg_match('/\b401\b/', $message) && str_contains($message, 'unauthorized'));
}
/**
* Validates and refreshes the access token if needed before API requests.
* This ensures long-running processes don't fail due to token expiration.
*
* @throws SocialAccountTokenInvalidException
*/
public function ensureValidToken(): void
{
if ($this->oauthAccount === null) {
return;
}
$newToken = $this->tokenManager->ensureValidToken($this->oauthAccount);
if ($newToken !== null) {
$this->accessToken = $newToken;
}
}
public function getConfig()
{
return $this->config;
}
// returns only active (archived=false)
public function getOwners(): array
{
return $this->getNewInstance()->crm()->owners()->getAll();
}
/**
* @param bool $archived
*
* @return array<Owner>|[]
*/
public function getOwnersArchived(bool $archived = true): array
{
$endpoint = '/crm/v3/owners';
$queryParams = [
'archived' => $archived ? 'true' : 'false',
];
$queryString = http_build_query($queryParams);
$owners = [];
try {
$response = $this->makeRequest(endpoint: $endpoint, queryString: $queryString);
$responseData = $response?->toArray();
foreach ($responseData['results'] as $result) {
try {
$owners[] = Owner::create($result);
} catch (Throwable $e) {
$this->log->error('[HubSpot] Failed to process owner data', [
'result' => $result,
'error' => $e->getMessage(),
]);
continue;
}
}
} catch (Throwable $e) {
$this->log->error('HubSpot] Failed to fetch owners', [
'archived' => $archived,
'error' => $e->getMessage(),
]);
return [];
}
return $owners;
}
public function getMeeting(string $engagementId): ObjectWithAssociations
{
return $this->getNewInstance()->crm()->objects()->basicApi()
->getById('meeting', $engagementId, null, 'contact,company,deal');
}
public function deleteEngagement(string $engagementId): void
{
$this->getInstance()->engagements()->delete((int) $engagementId);
}
public function getAssociationsData(array $ids, string $fromObject, string $toObject): array
{
$associationData = [];
$idChunks = array_chunk($ids, self::ASSOCIATIONS_BATCH_SIZE_LIMIT);
foreach ($idChunks as $idChunk) {
try {
$batchInput = new \HubSpot\Client\Crm\Associations\Model\BatchInputPublicObjectId();
$batchInput->setInputs(array_map(function ($id) {
$publicObjectId = new \HubSpot\Client\Crm\Associations\Model\PublicObjectId();
$publicObjectId->setId($id);
return $publicObjectId;
}, $idChunk));
$associatedObjectsData = $this
->getNewInstance()
->crm()
->associations()
->batchApi()
->read($fromObject, $toObject, $batchInput);
if ($associatedObjectsData instanceof \HubSpot\Client\Crm\Associations\Model\BatchResponsePublicAssociationMulti) {
foreach ($associatedObjectsData->getResults() as $association) {
$from = $association->getFrom()->getId();
$toAssociations = $association->getTo();
if (! empty($toAssociations)) {
$associationData[$from] = array_map(function ($item) {
return $item->getId();
}, $toAssociations);
}
}
}
// } catch (RateLimitException $e) {
// throw $e;
} catch (\Exception $e) {
$this->log->error('[Hubspot] Failed to fetch associations', [
'from_object' => $fromObject,
'to_object' => $toObject,
'reason' => $e->getMessage(),
]);
}
}
return $associationData;
}
/**
* @throws \Exception
*/
private function getNoteAssociationType(NoteObject $noteObject): string
{
return match($noteObject) {
NoteObject::Opportunity => 'note_to_deal',
NoteObject::Lead, NoteObject::Contact => 'note_to_contact', // or 'note_to_lead' if your portal supports it
NoteObject::Account => 'note_to_company',
NoteObject::Call, NoteObject::Event => throw new \Exception('Not supported'),
};
}
/**
* @throws \Exception
*/
private function getNoteObject(NoteObject $noteObject): string
{
return match($noteObject) {
NoteObject::Opportunity => 'deal',
NoteObject::Lead, NoteObject::Contact => 'contact',
NoteObject::Account => 'company',
NoteObject::Call, NoteObject::Event => throw new \Exception('Not supported'),
};
}
public function addAssociations(string $objectType, string $associationType, array $payload): Response
{
$endpoint = "/crm/v4/associations/$objectType/$associationType/batch/create";
return $this->makeRequest($endpoint, 'POST', $payload);
}
public function removeAssociations(string $objectType, string $associationType, array $payload): Response
{
$endpoint = "/crm/v4/associations/$objectType/$associationType/batch/archive";
return $this->makeRequest($endpoint, 'POST', $payload);
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
19
Previous Highlighted Error
Next Highlighted Error
[2026-05-07 14:21:15] local.INFO: [Hubspot] DEBUG Getting headers {
"headers":{
"Date":["Thu,07 May 2026 14:21:15 GMT"],
"Content-Type":["application/json;charset=utf-8"],
"Transfer-Encoding":["chunked"],
"Connection":["keep-alive"],
"CF-Ray":["9f80deb8db60dc3a-SOF"],
"CF-Cache-Status":["DYNAMIC"],
"Strict-Transport-Security":["max-age=31536000; includeSubDomains; preload"],
"Vary":["origin,
accept-encoding"],
"access-control-allow-credentials":["false"],
"server-timing":["hcid;desc=\"019e02d0-6fd8-7812-bdba-885b7ccb3ee3\",
cfr;desc=\"9f80deb8e7c6dc3a-IAD\""],
"x-content-type-options":["nosniff"],
"x-hubspot-correlation-id":["019e02d0-6fd8-7812-bdba-885b7ccb3ee3"],
"Set-Cookie":["__cf_bm=SIUrtdQgXVrik50pdqF6hZVYKhzTnQBidvMabeCtm0Y-1778163675-[IP_ADDRESS]-rI.ZggtDKxTge5zr8_2gbBfWMQQ.ufZEXDZyHz2mBUFdzdo2gTHEsOkXMSEShjK0hGYxNhUGM1ZoBpX7BcFZcHEjA7Cs_.SMUhUnd2nYjko; path=/; expires=Thu,
07-May-26 14:51:15 GMT; domain=.hubapi.com; HttpOnly; Secure; SameSite=None"],
"Report-To":["{
\"endpoints\":[{
\"url\":\"https:\\/\\/a.nel.cloudflare.com\\/report\\/v4?s=NYAlsVTP0fYm32qrSDjxYE4sd2RWRqiSp3wHsmdEgZlzoYdxI%2BIxVpHmsKn3O%2BKVA3mFIJ2m7YRECDGSM%2BW2IYTzo6FM4%2BdUIjURO8srzKSvJgZ%2BQ6R79arKQw3uHLlX\"}],
\"group\":\"cf-nel\",
\"max_age\":604800}"],
"NEL":["{
\"success_fraction\":0.01,
\"report_to\":\"cf-nel\",
\"max_age\":604800}"],
"Server":["cloudflare"]}} {
"correlation_id":"95236535-ec98-4541-b92a-adfa73b69eab",
"trace_id":"c7ab8365-903f-46d4-9403-0e5b551e3545"}
Project
Project...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"bounds":{"left":0.025930852,"top":0.019952115,"width":0.03856383,"height":0.025538707},"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JY-20725-handle-HS-search-rate-limit, menu","depth":5,"bounds":{"left":0.064494684,"top":0.019952115,"width":0.09541223,"height":0.025538707},"on_screen":true,"help_text":"Git Branch: JY-20725-handle-HS-search-rate-limit","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"bounds":{"left":0.82413566,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"HandleHubspotRateLimitTest","depth":6,"bounds":{"left":0.8394282,"top":0.019952115,"width":0.076130316,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'HandleHubspotRateLimitTest'","depth":6,"bounds":{"left":0.9155585,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'HandleHubspotRateLimitTest'","depth":6,"bounds":{"left":0.9268617,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"bounds":{"left":0.9381649,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"bounds":{"left":0.96609044,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"bounds":{"left":0.9773936,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"bounds":{"left":0.9886968,"top":0.019952115,"width":0.011303186,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"2","depth":4,"bounds":{"left":0.41156915,"top":0.19952115,"width":0.007978723,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"67","depth":4,"bounds":{"left":0.42154256,"top":0.19952115,"width":0.009973404,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"3","depth":4,"bounds":{"left":0.43351063,"top":0.19952115,"width":0.007978723,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.4431516,"top":0.19792499,"width":0.00731383,"height":0.018355945},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.4504654,"top":0.19792499,"width":0.006981383,"height":0.018355945},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Services\\Crm\\Hubspot;\n\nuse HubSpot\\Client\\Crm\\Deals\\ApiException as DealApiException;\nuse HubSpot\\Client\\Crm\\Contacts\\ApiException as ContactApiException;\nuse HubSpot\\Client\\Crm\\Companies\\ApiException as CompanyApiException;\nuse HubSpot\\Client\\Crm\\Contacts\\Model\\SimplePublicObjectWithAssociations as ContactsWithAssociations;\nuse HubSpot\\Client\\Crm\\Companies\\Model\\SimplePublicObjectWithAssociations as CompaniesWithAssociations;\nuse HubSpot\\Client\\Crm\\Deals\\Model\\SimplePublicObjectWithAssociations as DealWithAssociations;\nuse HubSpot\\Client\\Crm\\Objects\\Model\\SimplePublicObjectInput;\nuse HubSpot\\Client\\Crm\\Objects\\Model\\SimplePublicObjectWithAssociations as ObjectWithAssociations;\nuse HubSpot\\Client\\Crm\\Pipelines\\Model\\Error;\nuse HubSpot\\Client\\Crm\\Pipelines\\Model\\PipelineStage;\nuse HubSpot\\Client\\Crm\\Properties\\Model\\Property;\nuse HubSpot\\Discovery\\Discovery;\nuse Jiminny\\Exceptions\\CrmException;\nuse Jiminny\\Exceptions\\RateLimitException;\nuse Jiminny\\Exceptions\\SocialAccountTokenInvalidException;\nuse Jiminny\\Jobs\\Crm\\NoteObject;\nuse Jiminny\\Models\\Crm\\Field;\nuse Jiminny\\Services\\Crm\\BaseClient;\nuse Jiminny\\Services\\Crm\\Hubspot\\DTO\\Response\\Owner;\nuse Jiminny\\Services\\SocialAccountService;\nuse SevenShores\\Hubspot\\Exceptions\\BadRequest;\nuse SevenShores\\Hubspot\\Exceptions\\HubspotException;\nuse SevenShores\\Hubspot\\Factory;\nuse SevenShores\\Hubspot\\Http\\Response;\nuse Jiminny\\Services\\Crm\\Hubspot\\Pagination\\HubspotPaginationService;\nuse Illuminate\\Support\\Facades\\Redis;\nuse Throwable;\n\n/**\n * @phpstan-type CrmFieldOption array{id:string, label:string, value?:string}\n */\nclass Client extends BaseClient implements HubspotClientInterface\n{\n public const string MIN_API_VERSION = '2';\n\n public const string BASE_URL = 'https://api.hubapi.com';\n\n public const int ASSOCIATIONS_BATCH_SIZE_LIMIT = 1000;\n\n private HubspotPaginationService $paginationService;\n private HubspotTokenManager $tokenManager;\n\n public function __construct(\n SocialAccountService $socialAccountService,\n HubspotPaginationService $paginationService,\n HubspotTokenManager $tokenManager\n ) {\n parent::__construct($socialAccountService);\n $this->paginationService = $paginationService;\n $this->tokenManager = $tokenManager;\n\n $this->setBaseUrl(self::BASE_URL);\n $this->setVersion(self::MIN_API_VERSION);\n }\n\n /**\n * Reacts to a rate limits (429) from HubSpot by translating it\n * into a RateLimitException carrying retry_after.\n *\n * Wrap any outbound HubSpot call (SDK or raw HTTP) like:\n *\n * $this->executeRequest(fn () => $this->getNewInstance()->crm()->...);\n *\n * @template T\n * @param callable(): T $apiCall\n * @return T\n *\n * @throws RateLimitException\n */\n private function executeRequest(callable $apiCall)\n {\n $cacheKey = $this->getRateLimitCacheKey();\n\n $cachedRetryAfter = Redis::get($cacheKey);\n if (is_string($cachedRetryAfter) && is_numeric($cachedRetryAfter)) {\n throw new RateLimitException(\n 'Hubspot rate limit (cached circuit-breaker)',\n (int) $cachedRetryAfter,\n );\n }\n\n try {\n return $apiCall();\n } catch (Throwable $e) {\n if ($this->isHubspotRateLimit($e)) {\n $retryAfter = $this->parseRetryAfter($e);\n\n Redis::setex($cacheKey, $retryAfter, (string) $retryAfter);\n\n $this->log->warning('[Hubspot] Received 429 from API', [\n 'team_id' => $this->config->team_id,\n 'config_id' => $this->config->getId(),\n 'retry_after' => $retryAfter,\n 'message' => $this->parseMessage($e),\n 'reason' => $e->getMessage(),\n ]);\n\n throw new RateLimitException('Hubspot returned 429', $retryAfter, $e);\n }\n\n throw $e;\n }\n }\n\n private function getRateLimitCacheKey(): string\n {\n return sprintf('hubspot:ratelimit:portal:%d', $this->config->getId());\n }\n\n public function isHubspotRateLimit(Throwable $e): bool\n {\n if ($e instanceof BadRequest\n || $e instanceof DealApiException\n || $e instanceof ContactApiException\n || $e instanceof CompanyApiException\n || $e instanceof \\GuzzleHttp\\Exception\\RequestException\n ) {\n return (int) $e->getCode() === 429;\n }\n\n return false;\n }\n\n public function parseRetryAfter(Throwable $e): int\n {\n if (method_exists($e, 'getResponseHeaders')) {\n $headers = $e->getResponseHeaders() ?: [];\n $value = $headers['Retry-After'] ?? $headers['retry-after'] ?? null;\n if (is_array($value)) {\n $value = $value[0] ?? null;\n }\n if (is_numeric($value)) {\n return (int) $value;\n }\n }\n\n $message = strtolower($this->parseMessage($e));\n\n if (str_contains($message, 'daily')) {\n return 600;\n }\n if (str_contains($message, 'ten secondly')) {\n return 10;\n }\n if (str_contains($message, 'secondly')) {\n return 1;\n }\n\n $this->log->warning('[Hubspot] No retry-after header or known message, using default', [\n 'exception_class' => get_class($e),\n 'message' => $message,\n ]);\n\n return 10;\n }\n\n public function parseMessage(Throwable $e): string\n {\n if (! method_exists($e, 'getResponseBody')) {\n return '';\n }\n\n $body = $e->getResponseBody();\n if (is_string($body)) {\n $body = json_decode($body, true) ?? [];\n }\n\n if (! is_array($body)) {\n return '';\n }\n\n return (string) ($body['message'] ?? '');\n }\n\n public function getMinimumApiVersion(): string\n {\n return self::MIN_API_VERSION;\n }\n\n public function getInstance(): Factory\n {\n return new Factory([\n 'key' => $this->accessToken,\n 'oauth2' => true,\n 'base_url' => $this->baseUrl,\n ]);\n }\n\n public function getNewInstance(): Discovery\n {\n return \\HubSpot\\Factory::createWithAccessToken($this->accessToken);\n }\n\n /**\n * Secondly and daily limits for Hubspot API\n *\n * Product Tier: Free & Starter | Professional & Enterprise | API add-on (any tier)\n * Burst: 100/10 seconds | 150/10 seconds | 200/10 seconds\n * Daily: 250,000 | 500,000 | 1,000,000\n *\n * Official documentation states: The search endpoints are rate limited to five requests per second.\n * Since with 5 RPS were still hitting secondly rate limits we lowered it to 4\n */\n public function getPaginatedData(array $payload, string $type, int $offset = 0): array\n {\n $total = 0;\n $lastId = null;\n $rows = [];\n foreach ($this->getPaginatedDataGenerator($payload, $type, $offset, $total, $lastId) as $row) {\n $rows[] = $row;\n }\n\n return ['results' => $rows, 'total' => $total, 'last_record' => $lastId];\n }\n\n /**\n * @throws HubspotException\n * @throws SocialAccountTokenInvalidException\n * @throws BadRequest\n */\n public function getPaginatedDataGenerator(\n array $payload,\n string $type,\n int $offset = 0,\n int &$total = 0,\n ?string &$lastRecordId = null\n ): \\Generator {\n return $this->paginationService->getPaginatedDataGenerator(\n $this,\n $payload,\n $type,\n $offset,\n $total,\n $lastRecordId\n );\n }\n\n /**\n * Execute a search request against HubSpot CRM objects with rate limiting.\n *\n * @param string $objectType The object type ('deals', 'companies', 'contacts', 'calls')\n * @param array<string, mixed> $payload The search payload with filters, sorts, properties, etc.\n * @return array The search response with 'results', 'total', 'paging' keys\n * @throws RateLimitException When rate limit is hit\n * @throws HubspotException On API errors\n */\n public function search(string $objectType, array $payload): array\n {\n $endpoint = self::BASE_URL . \"/crm/v3/objects/{$objectType}/search\";\n\n return $this->executeRequest(function () use ($endpoint, $payload) {\n $response = $this->getInstance()->getClient()->request('POST', $endpoint, ['json' => $payload]);\n\n return $response->toArray();\n });\n }\n\n /**\n * @throws DealApiException\n * @throws CrmException\n */\n public function getOpportunityById(string $crmId, array $fields): array\n {\n try {\n// $deal = $this->executeRequest(fn () => $this->getNewInstance()->crm()->deals()->basicApi()->getById(\n $deal = $this->getNewInstance()->crm()->deals()->basicApi()->getById(\n $crmId,\n implode(',', $fields),\n 'companies,contacts'\n );\n } catch (DealApiException $e) {\n $this->log->info('[Hubspot] Failed to fetch opportunity', [\n 'crm_id' => $crmId,\n 'reason' => $e->getMessage(),\n ]);\n\n throw $e;\n }\n\n if (! $deal instanceof DealWithAssociations) {\n throw new CrmException('Deal not found');\n }\n\n return [\n 'id' => $deal->getId(),\n 'properties' => $deal->getProperties(),\n 'associations' => $deal->getAssociations(),\n ];\n }\n\n /**\n * Generic batch read method for HubSpot objects\n *\n * @param string $objectType The object type ('deals', 'companies', 'contacts')\n * @param array<string> $crmIds Array of HubSpot object IDs (max 100)\n * @param array<string> $fields Array of property names to fetch\n *\n * @return array<string, array> Array keyed by CRM ID with object data\n */\n private function batchReadObjects(string $objectType, array $crmIds, array $fields): array\n {\n if (empty($crmIds)) {\n return [];\n }\n\n $this->validateBatchSize($objectType, $crmIds);\n $this->ensureValidToken();\n\n try {\n $batchConfig = $this->createBatchConfiguration($objectType);\n $batchReadRequest = $this->prepareBatchRequest($batchConfig, $crmIds, $fields);\n $response = $batchConfig['api']->read($batchReadRequest);\n\n $this->validateApiResponse($response, $objectType);\n\n $results = $this->processApiResults($response);\n $this->logBatchResults($objectType, $crmIds, $results);\n\n return $results;\n } catch (\\Throwable $e) {\n $this->handleBatchError($e, $objectType, $crmIds);\n }\n }\n\n private function validateBatchSize(string $objectType, array $crmIds): void\n {\n if (count($crmIds) > 100) {\n throw new \\InvalidArgumentException(\"Batch size cannot exceed 100 {$objectType}\");\n }\n }\n\n private function createBatchConfiguration(string $objectType): array\n {\n $configurations = [\n 'deals' => [\n 'batchReadRequest' => new \\HubSpot\\Client\\Crm\\Deals\\Model\\BatchReadInputSimplePublicObjectId(),\n 'inputClass' => \\HubSpot\\Client\\Crm\\Deals\\Model\\SimplePublicObjectId::class,\n 'api' => $this->getNewInstance()->crm()->deals()->batchApi(),\n ],\n 'companies' => [\n 'batchReadRequest' => new \\HubSpot\\Client\\Crm\\Companies\\Model\\BatchReadInputSimplePublicObjectId(),\n 'inputClass' => \\HubSpot\\Client\\Crm\\Companies\\Model\\SimplePublicObjectId::class,\n 'api' => $this->getNewInstance()->crm()->companies()->batchApi(),\n ],\n 'contacts' => [\n 'batchReadRequest' => new \\HubSpot\\Client\\Crm\\Contacts\\Model\\BatchReadInputSimplePublicObjectId(),\n 'inputClass' => \\HubSpot\\Client\\Crm\\Contacts\\Model\\SimplePublicObjectId::class,\n 'api' => $this->getNewInstance()->crm()->contacts()->batchApi(),\n ],\n ];\n\n if (! isset($configurations[$objectType])) {\n throw new \\InvalidArgumentException(\"Unsupported object type: {$objectType}\");\n }\n\n return $configurations[$objectType];\n }\n\n private function prepareBatchRequest(array $batchConfig, array $crmIds, array $fields): object\n {\n $batchReadRequest = $batchConfig['batchReadRequest'];\n $inputClass = $batchConfig['inputClass'];\n\n $inputs = array_map(function ($crmId) use ($inputClass) {\n $input = new $inputClass();\n $input->setId($crmId);\n\n return $input;\n }, $crmIds);\n\n $batchReadRequest->setInputs($inputs);\n $batchReadRequest->setProperties($fields);\n\n return $batchReadRequest;\n }\n\n private function validateApiResponse($response, string $objectType): void\n {\n if (! $response) {\n throw new CrmException(\"HubSpot API returned null response for {$objectType} batch read\");\n }\n }\n\n private function processApiResults($response): array\n {\n $results = [];\n $responseResults = $response->getResults();\n\n if ($responseResults) {\n foreach ($responseResults as $object) {\n if ($object && $object->getId()) {\n $results[$object->getId()] = [\n 'id' => $object->getId(),\n 'properties' => $object->getProperties() ?: [],\n ];\n }\n }\n }\n\n return $results;\n }\n\n private function logBatchResults(string $objectType, array $crmIds, array $results): void\n {\n $this->log->info(\"[HubSpot] Batch fetched {$objectType}\", [\n 'requested_count' => count($crmIds),\n 'returned_count' => count($results),\n 'crm_ids' => $crmIds,\n ]);\n }\n\n private function handleBatchError(\\Throwable $e, string $objectType, array $crmIds): void\n {\n $errorMessage = $e->getMessage() ?: 'Unknown error';\n $errorTrace = $e->getTraceAsString() ?: 'No trace available';\n\n $this->log->error(\"[HubSpot] Failed to batch fetch {$objectType}\", [\n 'crm_ids' => $crmIds,\n 'error' => $errorMessage,\n 'trace' => $errorTrace,\n ]);\n\n throw new CrmException(\"Failed to batch fetch {$objectType}: \" . $errorMessage);\n }\n\n /**\n * Batch read multiple opportunities by their CRM IDs\n *\n * @param array<string> $crmIds Array of HubSpot deal IDs (max 100)\n * @param array<string> $fields Array of property names to fetch\n *\n * @return array<string, array> Array keyed by CRM ID with opportunity data\n */\n public function getOpportunitiesByIds(array $crmIds, array $fields): array\n {\n return $this->batchReadObjects('deals', $crmIds, $fields);\n }\n\n /**\n * Batch read multiple companies by their CRM IDs\n *\n * @param array<string> $crmIds Array of HubSpot company IDs (max 100)\n * @param array<string> $fields Array of property names to fetch\n *\n * @return array<string, array> Array keyed by CRM ID with company data\n */\n public function getCompaniesByIds(array $crmIds, array $fields): array\n {\n return $this->batchReadObjects('companies', $crmIds, $fields);\n }\n\n /**\n * Batch read multiple contacts by their CRM IDs\n *\n * @param array<string> $crmIds Array of HubSpot contact IDs (max 100)\n * @param array<string> $fields Array of property names to fetch\n *\n * @return array<string, array> Array keyed by CRM ID with contact data\n */\n public function getContactsByIds(array $crmIds, array $fields): array\n {\n return $this->batchReadObjects('contacts', $crmIds, $fields);\n }\n\n /**\n * @throws CompanyApiException\n * @throws CrmException\n */\n public function getAccountById(string $crmId, array $fields): array\n {\n try {\n $company = $this->getNewInstance()->crm()->companies()->basicApi()->getById(\n $crmId,\n implode(',', $fields),\n );\n } catch (CompanyApiException $e) {\n $this->log->info('[Hubspot] Failed to fetch account', [\n 'crm_id' => $crmId,\n 'reason' => $e->getMessage(),\n ]);\n\n throw $e;\n }\n\n if (! $company instanceof CompaniesWithAssociations) {\n throw new CrmException('Account not found');\n }\n\n return [\n 'id' => $company->getId(),\n 'properties' => $company->getProperties(),\n ];\n }\n\n /**\n * @throws ContactApiException\n * @throws CrmException\n */\n public function getContactById(string $crmId, array $fields): array\n {\n try {\n $contact = $this->getNewInstance()->crm()->contacts()->basicApi()->getById(\n $crmId,\n implode(',', $fields)\n );\n } catch (ContactApiException $e) {\n $this->log->info('[Hubspot] Failed to fetch contact', [\n 'crm_id' => $crmId,\n 'reason' => $e->getMessage(),\n ]);\n\n throw $e;\n }\n\n if (! $contact instanceof ContactsWithAssociations) {\n throw new CrmException('Contact not found');\n }\n\n return [\n 'id' => $contact->getId(),\n 'properties' => $contact->getProperties(),\n ];\n }\n\n /**\n * This is email search request that Hubspot offers as GET (more generous quota)\n */\n public function getContactByEmail(string $email, array $fields = []): array\n {\n try {\n $contact = $this->getNewInstance()->crm()->contacts()->basicApi()->getById(\n $email,\n implode(',', $fields),\n null,\n false,\n 'email'\n );\n\n return [\n 'id' => $contact->getId(),\n 'properties' => $contact->getProperties(),\n ];\n } catch (ContactApiException $e) {\n $this->log->info('[Hubspot] Failed to fetch contact', [\n 'email' => $email,\n 'reason' => $e->getMessage(),\n ]);\n\n return [];\n }\n }\n\n /**\n * @throws CrmException\n */\n public function fetchProperty(string $objectType, string $propertyId): Property\n {\n $result = $this->getNewInstance()->crm()->properties()->coreApi()->getByName($objectType, $propertyId);\n\n if (! $result instanceof Property) {\n $this->log->error('[Hubspot] Failed to fetch property', [\n 'object_type' => $objectType,\n 'property_id' => $propertyId,\n 'reason' => $result->getMessage(),\n ]);\n\n throw new CrmException('Failed to fetch property');\n }\n\n return $result;\n }\n\n /**\n * @return array<CrmFieldOption>\n */\n public function fetchPropertyOptions(string $objectType, string $propertyId): array\n {\n /** @var array<CrmFieldOption> */\n return $this->fetchProperty($objectType, $propertyId)->getOptions();\n }\n\n /**\n * @return array<array{id:string, label:string, deleted:bool}>\n */\n public function fetchCallDispositions(): array\n {\n /** @var Response $response */\n $response = $this->getInstance()->engagements()->getCallDispositions();\n\n /**\n * @var array<array{\n * id:string,\n * label:string,\n * deleted: bool\n * }>\n */\n return $response->toArray();\n }\n\n /**\n * @return array<CrmFieldOption>\n */\n public function fetchOpportunityPipelineStages(): array\n {\n $stages = [];\n $apiResponse = $this->getNewInstance()->crm()->pipelines()->pipelinesApi()->getAll('deals');\n\n if ($apiResponse instanceof Error) {\n $this->log->error('[Hubspot] Failed to fetch opportunity pipelines', [\n 'reason' => $apiResponse->getMessage(),\n ]);\n\n return [];\n }\n\n foreach ($apiResponse->getResults() as $pipeline) {\n $pipelineStages = array_map(\n static function (PipelineStage $stage) {\n return [\n 'id' => $stage->getId(),\n 'label' => $stage->getLabel(),\n ];\n },\n $pipeline->getStages()\n );\n\n $stages = array_merge($stages, $pipelineStages);\n }\n\n return $stages;\n }\n\n public function fetchOpportunityPipelines(): array\n {\n $pipelines = [];\n\n try {\n $apiResponse = $this->makeRequest('/crm/v3/pipelines/deals');\n } catch (\\Exception $e) {\n $this->log->info('[Hubspot] Failed to fetch opportunity pipelines', [\n 'reason' => $e->getMessage(),\n ]);\n\n return [];\n }\n\n $response = $apiResponse->toArray();\n\n foreach ($response['results'] as $pipeline) {\n $pipelines[] = [\n 'id' => $pipeline['id'],\n 'label' => $pipeline['label'],\n ];\n }\n\n return $pipelines;\n }\n\n /**\n * @return array<CrmFieldOption>\n */\n public function fetchMeetingOutcomeFieldOptions(Field $field): array\n {\n return $field->getCrmProviderId() === 'meetingOutcome'\n ? $this->fetchMeetingOutcomeTypes()\n : $this->fetchCallActivityTypes();\n }\n\n public function fetchMeetingOutcomeTypes(): array\n {\n return $this->extractMeetingTypeOptions(\n 'https://api.hubapi.com/crm/v3/properties/meeting/hs_meeting_outcome'\n );\n }\n\n public function fetchCallActivityTypes(): array\n {\n return $this->extractMeetingTypeOptions(\n 'https://api.hubapi.com/crm/v3/properties/call/hs_activity_type'\n );\n }\n\n private function extractMeetingTypeOptions(string $endpoint): array\n {\n /** @var Response $response */\n $response = $this->getInstance()\n ->getClient()\n ->request('GET', $endpoint);\n\n /**\n * @var array<array{\n * value: string,\n * label: string,\n * displayOrder: int\n * }> $optionData\n */\n $optionData = $response->toArray()['options'] ?? [];\n\n $options = [];\n foreach ($optionData as $item) {\n $options[] = [\n 'id' => $item['value'],\n 'value' => $item['value'],\n 'label' => $item['label'],\n 'display_order' => $item['displayOrder'],\n ];\n }\n\n return $options;\n }\n\n /**\n * @return array<CrmFieldOption>\n */\n public function fetchDispositionFieldOptions(): array\n {\n $options = [];\n\n $dispositions = $this->fetchCallDispositions();\n\n foreach ($dispositions as $disposition) {\n if ($disposition['deleted'] !== false) {\n continue;\n }\n\n $option['value'] = $disposition['id'];\n $option['id'] = $disposition['id'];\n $option['label'] = $disposition['label'];\n\n $options[] = $option;\n }\n\n return $options;\n }\n\n /**\n * @return array<CrmFieldOption>\n */\n public function fetchOpportunityFieldOptions(Field $field): array\n {\n if ($field->isStageField()) {\n return $this->fetchOpportunityPipelineStages();\n }\n\n if ($field->isPipelineField()) {\n return $this->fetchOpportunityPipelines();\n }\n\n return $this->fetchPropertyOptions('deals', $field->getCrmProviderId());\n }\n\n /**\n * @throws BadRequest\n * @throws HubspotException\n */\n public function makeRequest(string $endpoint, $method = 'GET', $payload = [], ?string $queryString = null)\n {\n $endpoint = self::BASE_URL . $endpoint;\n\n if ($method === 'GET') {\n return $this->getInstance()->getClient()?->request(\n method: $method,\n endpoint: $endpoint,\n query_string: $queryString\n );\n } else {\n return $this->getInstance()->getClient()->request($method, $endpoint, [\n 'json' => ($payload),\n ]);\n }\n }\n\n /**\n * @throws BadRequest\n * @throws HubspotException\n */\n public function createMeeting(array $payload): Response\n {\n $endpoint = '/crm/v3/objects/meetings';\n\n return $this->makeRequest($endpoint, 'POST', $payload);\n }\n\n /**\n * @throws BadRequest\n * @throws HubspotException\n */\n public function updateMeeting(string $meetingId, array $payload): Response\n {\n $endpoint = '/crm/v3/objects/meetings/' . $meetingId;\n\n return $this->makeRequest($endpoint, 'PATCH', $payload);\n }\n\n /**\n * @throws \\Exception\n */\n public function createNote(\n string $body,\n string $ownerId,\n int $timestamp,\n string $objectId,\n NoteObject $noteObject\n ): ?string {\n try {\n $noteInput = new SimplePublicObjectInput([\n 'properties' => [\n 'hs_note_body' => $body,\n 'hubspot_owner_id' => $ownerId,\n 'hs_timestamp' => $timestamp,\n ],\n ]);\n\n // Create note\n $note = $this->getNewInstance()->crm()->objects()->basicApi()->create('note', $noteInput);\n\n $this->getNewInstance()->crm()->objects()->associationsApi()->create(\n 'note',\n $note->getId(),\n $this->getNoteObject($noteObject),\n $objectId,\n $this->getNoteAssociationType($noteObject),\n );\n\n return $note->getId();\n } catch (\\Exception $e) {\n $this->log->error('[Hubspot] Failed to create note', [\n 'objectId' => $objectId,\n 'noteObject' => $noteObject->getObjectType(),\n 'reason' => $e->getMessage(),\n ]);\n\n \\Sentry::captureException($e);\n }\n\n return null;\n }\n\n public function updateEngagement(string $objectId, array $engagement, array $metadata): void\n {\n $this->getInstance()->engagements()->update($objectId, $engagement, $metadata);\n }\n\n public function getEngagementData(string $engagementId): array\n {\n $engagement = $this->getInstance()->engagements()->get($engagementId);\n\n return $engagement->toArray();\n }\n\n public function createEngagement(array $engagement, array $associations, array $metadata): Response\n {\n return $this->getInstance()\n ->engagements()\n ->create($engagement, $associations, $metadata);\n }\n\n public function isUnauthorizedException(\\Exception $e): bool\n {\n // Check for specific HubSpot API exception types first\n if ($e instanceof BadRequest) {\n // BadRequest can contain 401 status codes\n return $e->getCode() === 401;\n }\n\n // Check for HTTP client exceptions with status codes\n if ($e instanceof \\GuzzleHttp\\Exception\\RequestException && $e->hasResponse()) {\n $response = $e->getResponse();\n if ($response !== null) {\n return $response->getStatusCode() === 401;\n }\n }\n\n // Check for Guzzle HTTP exceptions\n if ($e instanceof \\GuzzleHttp\\Exception\\ClientException) {\n return $e->getCode() === 401;\n }\n\n // Fallback to string matching as last resort, but be more specific\n $message = strtolower($e->getMessage());\n\n return str_contains($message, '401 unauthorized') ||\n str_contains($message, 'http 401') ||\n str_contains($message, 'status code 401') ||\n (preg_match('/\\b401\\b/', $message) && str_contains($message, 'unauthorized'));\n }\n\n /**\n * Validates and refreshes the access token if needed before API requests.\n * This ensures long-running processes don't fail due to token expiration.\n *\n * @throws SocialAccountTokenInvalidException\n */\n public function ensureValidToken(): void\n {\n if ($this->oauthAccount === null) {\n return;\n }\n\n $newToken = $this->tokenManager->ensureValidToken($this->oauthAccount);\n if ($newToken !== null) {\n $this->accessToken = $newToken;\n }\n }\n\n public function getConfig()\n {\n return $this->config;\n }\n\n // returns only active (archived=false)\n public function getOwners(): array\n {\n return $this->getNewInstance()->crm()->owners()->getAll();\n }\n\n /**\n * @param bool $archived\n *\n * @return array<Owner>|[]\n */\n public function getOwnersArchived(bool $archived = true): array\n {\n $endpoint = '/crm/v3/owners';\n $queryParams = [\n 'archived' => $archived ? 'true' : 'false',\n ];\n $queryString = http_build_query($queryParams);\n\n $owners = [];\n\n try {\n $response = $this->makeRequest(endpoint: $endpoint, queryString: $queryString);\n $responseData = $response?->toArray();\n\n foreach ($responseData['results'] as $result) {\n try {\n $owners[] = Owner::create($result);\n } catch (Throwable $e) {\n $this->log->error('[HubSpot] Failed to process owner data', [\n 'result' => $result,\n 'error' => $e->getMessage(),\n ]);\n\n continue;\n }\n }\n } catch (Throwable $e) {\n $this->log->error('HubSpot] Failed to fetch owners', [\n 'archived' => $archived,\n 'error' => $e->getMessage(),\n ]);\n\n return [];\n }\n\n return $owners;\n }\n\n public function getMeeting(string $engagementId): ObjectWithAssociations\n {\n return $this->getNewInstance()->crm()->objects()->basicApi()\n ->getById('meeting', $engagementId, null, 'contact,company,deal');\n }\n\n public function deleteEngagement(string $engagementId): void\n {\n $this->getInstance()->engagements()->delete((int) $engagementId);\n }\n\n public function getAssociationsData(array $ids, string $fromObject, string $toObject): array\n {\n $associationData = [];\n $idChunks = array_chunk($ids, self::ASSOCIATIONS_BATCH_SIZE_LIMIT);\n\n foreach ($idChunks as $idChunk) {\n try {\n $batchInput = new \\HubSpot\\Client\\Crm\\Associations\\Model\\BatchInputPublicObjectId();\n $batchInput->setInputs(array_map(function ($id) {\n $publicObjectId = new \\HubSpot\\Client\\Crm\\Associations\\Model\\PublicObjectId();\n $publicObjectId->setId($id);\n\n return $publicObjectId;\n }, $idChunk));\n\n $associatedObjectsData = $this\n ->getNewInstance()\n ->crm()\n ->associations()\n ->batchApi()\n ->read($fromObject, $toObject, $batchInput);\n\n if ($associatedObjectsData instanceof \\HubSpot\\Client\\Crm\\Associations\\Model\\BatchResponsePublicAssociationMulti) {\n foreach ($associatedObjectsData->getResults() as $association) {\n $from = $association->getFrom()->getId();\n $toAssociations = $association->getTo();\n\n if (! empty($toAssociations)) {\n $associationData[$from] = array_map(function ($item) {\n return $item->getId();\n }, $toAssociations);\n }\n }\n }\n// } catch (RateLimitException $e) {\n// throw $e;\n } catch (\\Exception $e) {\n $this->log->error('[Hubspot] Failed to fetch associations', [\n 'from_object' => $fromObject,\n 'to_object' => $toObject,\n 'reason' => $e->getMessage(),\n ]);\n }\n }\n\n return $associationData;\n }\n\n /**\n * @throws \\Exception\n */\n private function getNoteAssociationType(NoteObject $noteObject): string\n {\n return match($noteObject) {\n NoteObject::Opportunity => 'note_to_deal',\n NoteObject::Lead, NoteObject::Contact => 'note_to_contact', // or 'note_to_lead' if your portal supports it\n NoteObject::Account => 'note_to_company',\n NoteObject::Call, NoteObject::Event => throw new \\Exception('Not supported'),\n };\n }\n\n /**\n * @throws \\Exception\n */\n private function getNoteObject(NoteObject $noteObject): string\n {\n return match($noteObject) {\n NoteObject::Opportunity => 'deal',\n NoteObject::Lead, NoteObject::Contact => 'contact',\n NoteObject::Account => 'company',\n NoteObject::Call, NoteObject::Event => throw new \\Exception('Not supported'),\n };\n }\n\n public function addAssociations(string $objectType, string $associationType, array $payload): Response\n {\n $endpoint = \"/crm/v4/associations/$objectType/$associationType/batch/create\";\n\n return $this->makeRequest($endpoint, 'POST', $payload);\n }\n\n public function removeAssociations(string $objectType, string $associationType, array $payload): Response\n {\n $endpoint = \"/crm/v4/associations/$objectType/$associationType/batch/archive\";\n\n return $this->makeRequest($endpoint, 'POST', $payload);\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Services\\Crm\\Hubspot;\n\nuse HubSpot\\Client\\Crm\\Deals\\ApiException as DealApiException;\nuse HubSpot\\Client\\Crm\\Contacts\\ApiException as ContactApiException;\nuse HubSpot\\Client\\Crm\\Companies\\ApiException as CompanyApiException;\nuse HubSpot\\Client\\Crm\\Contacts\\Model\\SimplePublicObjectWithAssociations as ContactsWithAssociations;\nuse HubSpot\\Client\\Crm\\Companies\\Model\\SimplePublicObjectWithAssociations as CompaniesWithAssociations;\nuse HubSpot\\Client\\Crm\\Deals\\Model\\SimplePublicObjectWithAssociations as DealWithAssociations;\nuse HubSpot\\Client\\Crm\\Objects\\Model\\SimplePublicObjectInput;\nuse HubSpot\\Client\\Crm\\Objects\\Model\\SimplePublicObjectWithAssociations as ObjectWithAssociations;\nuse HubSpot\\Client\\Crm\\Pipelines\\Model\\Error;\nuse HubSpot\\Client\\Crm\\Pipelines\\Model\\PipelineStage;\nuse HubSpot\\Client\\Crm\\Properties\\Model\\Property;\nuse HubSpot\\Discovery\\Discovery;\nuse Jiminny\\Exceptions\\CrmException;\nuse Jiminny\\Exceptions\\RateLimitException;\nuse Jiminny\\Exceptions\\SocialAccountTokenInvalidException;\nuse Jiminny\\Jobs\\Crm\\NoteObject;\nuse Jiminny\\Models\\Crm\\Field;\nuse Jiminny\\Services\\Crm\\BaseClient;\nuse Jiminny\\Services\\Crm\\Hubspot\\DTO\\Response\\Owner;\nuse Jiminny\\Services\\SocialAccountService;\nuse SevenShores\\Hubspot\\Exceptions\\BadRequest;\nuse SevenShores\\Hubspot\\Exceptions\\HubspotException;\nuse SevenShores\\Hubspot\\Factory;\nuse SevenShores\\Hubspot\\Http\\Response;\nuse Jiminny\\Services\\Crm\\Hubspot\\Pagination\\HubspotPaginationService;\nuse Illuminate\\Support\\Facades\\Redis;\nuse Throwable;\n\n/**\n * @phpstan-type CrmFieldOption array{id:string, label:string, value?:string}\n */\nclass Client extends BaseClient implements HubspotClientInterface\n{\n public const string MIN_API_VERSION = '2';\n\n public const string BASE_URL = 'https://api.hubapi.com';\n\n public const int ASSOCIATIONS_BATCH_SIZE_LIMIT = 1000;\n\n private HubspotPaginationService $paginationService;\n private HubspotTokenManager $tokenManager;\n\n public function __construct(\n SocialAccountService $socialAccountService,\n HubspotPaginationService $paginationService,\n HubspotTokenManager $tokenManager\n ) {\n parent::__construct($socialAccountService);\n $this->paginationService = $paginationService;\n $this->tokenManager = $tokenManager;\n\n $this->setBaseUrl(self::BASE_URL);\n $this->setVersion(self::MIN_API_VERSION);\n }\n\n /**\n * Reacts to a rate limits (429) from HubSpot by translating it\n * into a RateLimitException carrying retry_after.\n *\n * Wrap any outbound HubSpot call (SDK or raw HTTP) like:\n *\n * $this->executeRequest(fn () => $this->getNewInstance()->crm()->...);\n *\n * @template T\n * @param callable(): T $apiCall\n * @return T\n *\n * @throws RateLimitException\n */\n private function executeRequest(callable $apiCall)\n {\n $cacheKey = $this->getRateLimitCacheKey();\n\n $cachedRetryAfter = Redis::get($cacheKey);\n if (is_string($cachedRetryAfter) && is_numeric($cachedRetryAfter)) {\n throw new RateLimitException(\n 'Hubspot rate limit (cached circuit-breaker)',\n (int) $cachedRetryAfter,\n );\n }\n\n try {\n return $apiCall();\n } catch (Throwable $e) {\n if ($this->isHubspotRateLimit($e)) {\n $retryAfter = $this->parseRetryAfter($e);\n\n Redis::setex($cacheKey, $retryAfter, (string) $retryAfter);\n\n $this->log->warning('[Hubspot] Received 429 from API', [\n 'team_id' => $this->config->team_id,\n 'config_id' => $this->config->getId(),\n 'retry_after' => $retryAfter,\n 'message' => $this->parseMessage($e),\n 'reason' => $e->getMessage(),\n ]);\n\n throw new RateLimitException('Hubspot returned 429', $retryAfter, $e);\n }\n\n throw $e;\n }\n }\n\n private function getRateLimitCacheKey(): string\n {\n return sprintf('hubspot:ratelimit:portal:%d', $this->config->getId());\n }\n\n public function isHubspotRateLimit(Throwable $e): bool\n {\n if ($e instanceof BadRequest\n || $e instanceof DealApiException\n || $e instanceof ContactApiException\n || $e instanceof CompanyApiException\n || $e instanceof \\GuzzleHttp\\Exception\\RequestException\n ) {\n return (int) $e->getCode() === 429;\n }\n\n return false;\n }\n\n public function parseRetryAfter(Throwable $e): int\n {\n if (method_exists($e, 'getResponseHeaders')) {\n $headers = $e->getResponseHeaders() ?: [];\n $value = $headers['Retry-After'] ?? $headers['retry-after'] ?? null;\n if (is_array($value)) {\n $value = $value[0] ?? null;\n }\n if (is_numeric($value)) {\n return (int) $value;\n }\n }\n\n $message = strtolower($this->parseMessage($e));\n\n if (str_contains($message, 'daily')) {\n return 600;\n }\n if (str_contains($message, 'ten secondly')) {\n return 10;\n }\n if (str_contains($message, 'secondly')) {\n return 1;\n }\n\n $this->log->warning('[Hubspot] No retry-after header or known message, using default', [\n 'exception_class' => get_class($e),\n 'message' => $message,\n ]);\n\n return 10;\n }\n\n public function parseMessage(Throwable $e): string\n {\n if (! method_exists($e, 'getResponseBody')) {\n return '';\n }\n\n $body = $e->getResponseBody();\n if (is_string($body)) {\n $body = json_decode($body, true) ?? [];\n }\n\n if (! is_array($body)) {\n return '';\n }\n\n return (string) ($body['message'] ?? '');\n }\n\n public function getMinimumApiVersion(): string\n {\n return self::MIN_API_VERSION;\n }\n\n public function getInstance(): Factory\n {\n return new Factory([\n 'key' => $this->accessToken,\n 'oauth2' => true,\n 'base_url' => $this->baseUrl,\n ]);\n }\n\n public function getNewInstance(): Discovery\n {\n return \\HubSpot\\Factory::createWithAccessToken($this->accessToken);\n }\n\n /**\n * Secondly and daily limits for Hubspot API\n *\n * Product Tier: Free & Starter | Professional & Enterprise | API add-on (any tier)\n * Burst: 100/10 seconds | 150/10 seconds | 200/10 seconds\n * Daily: 250,000 | 500,000 | 1,000,000\n *\n * Official documentation states: The search endpoints are rate limited to five requests per second.\n * Since with 5 RPS were still hitting secondly rate limits we lowered it to 4\n */\n public function getPaginatedData(array $payload, string $type, int $offset = 0): array\n {\n $total = 0;\n $lastId = null;\n $rows = [];\n foreach ($this->getPaginatedDataGenerator($payload, $type, $offset, $total, $lastId) as $row) {\n $rows[] = $row;\n }\n\n return ['results' => $rows, 'total' => $total, 'last_record' => $lastId];\n }\n\n /**\n * @throws HubspotException\n * @throws SocialAccountTokenInvalidException\n * @throws BadRequest\n */\n public function getPaginatedDataGenerator(\n array $payload,\n string $type,\n int $offset = 0,\n int &$total = 0,\n ?string &$lastRecordId = null\n ): \\Generator {\n return $this->paginationService->getPaginatedDataGenerator(\n $this,\n $payload,\n $type,\n $offset,\n $total,\n $lastRecordId\n );\n }\n\n /**\n * Execute a search request against HubSpot CRM objects with rate limiting.\n *\n * @param string $objectType The object type ('deals', 'companies', 'contacts', 'calls')\n * @param array<string, mixed> $payload The search payload with filters, sorts, properties, etc.\n * @return array The search response with 'results', 'total', 'paging' keys\n * @throws RateLimitException When rate limit is hit\n * @throws HubspotException On API errors\n */\n public function search(string $objectType, array $payload): array\n {\n $endpoint = self::BASE_URL . \"/crm/v3/objects/{$objectType}/search\";\n\n return $this->executeRequest(function () use ($endpoint, $payload) {\n $response = $this->getInstance()->getClient()->request('POST', $endpoint, ['json' => $payload]);\n\n return $response->toArray();\n });\n }\n\n /**\n * @throws DealApiException\n * @throws CrmException\n */\n public function getOpportunityById(string $crmId, array $fields): array\n {\n try {\n// $deal = $this->executeRequest(fn () => $this->getNewInstance()->crm()->deals()->basicApi()->getById(\n $deal = $this->getNewInstance()->crm()->deals()->basicApi()->getById(\n $crmId,\n implode(',', $fields),\n 'companies,contacts'\n );\n } catch (DealApiException $e) {\n $this->log->info('[Hubspot] Failed to fetch opportunity', [\n 'crm_id' => $crmId,\n 'reason' => $e->getMessage(),\n ]);\n\n throw $e;\n }\n\n if (! $deal instanceof DealWithAssociations) {\n throw new CrmException('Deal not found');\n }\n\n return [\n 'id' => $deal->getId(),\n 'properties' => $deal->getProperties(),\n 'associations' => $deal->getAssociations(),\n ];\n }\n\n /**\n * Generic batch read method for HubSpot objects\n *\n * @param string $objectType The object type ('deals', 'companies', 'contacts')\n * @param array<string> $crmIds Array of HubSpot object IDs (max 100)\n * @param array<string> $fields Array of property names to fetch\n *\n * @return array<string, array> Array keyed by CRM ID with object data\n */\n private function batchReadObjects(string $objectType, array $crmIds, array $fields): array\n {\n if (empty($crmIds)) {\n return [];\n }\n\n $this->validateBatchSize($objectType, $crmIds);\n $this->ensureValidToken();\n\n try {\n $batchConfig = $this->createBatchConfiguration($objectType);\n $batchReadRequest = $this->prepareBatchRequest($batchConfig, $crmIds, $fields);\n $response = $batchConfig['api']->read($batchReadRequest);\n\n $this->validateApiResponse($response, $objectType);\n\n $results = $this->processApiResults($response);\n $this->logBatchResults($objectType, $crmIds, $results);\n\n return $results;\n } catch (\\Throwable $e) {\n $this->handleBatchError($e, $objectType, $crmIds);\n }\n }\n\n private function validateBatchSize(string $objectType, array $crmIds): void\n {\n if (count($crmIds) > 100) {\n throw new \\InvalidArgumentException(\"Batch size cannot exceed 100 {$objectType}\");\n }\n }\n\n private function createBatchConfiguration(string $objectType): array\n {\n $configurations = [\n 'deals' => [\n 'batchReadRequest' => new \\HubSpot\\Client\\Crm\\Deals\\Model\\BatchReadInputSimplePublicObjectId(),\n 'inputClass' => \\HubSpot\\Client\\Crm\\Deals\\Model\\SimplePublicObjectId::class,\n 'api' => $this->getNewInstance()->crm()->deals()->batchApi(),\n ],\n 'companies' => [\n 'batchReadRequest' => new \\HubSpot\\Client\\Crm\\Companies\\Model\\BatchReadInputSimplePublicObjectId(),\n 'inputClass' => \\HubSpot\\Client\\Crm\\Companies\\Model\\SimplePublicObjectId::class,\n 'api' => $this->getNewInstance()->crm()->companies()->batchApi(),\n ],\n 'contacts' => [\n 'batchReadRequest' => new \\HubSpot\\Client\\Crm\\Contacts\\Model\\BatchReadInputSimplePublicObjectId(),\n 'inputClass' => \\HubSpot\\Client\\Crm\\Contacts\\Model\\SimplePublicObjectId::class,\n 'api' => $this->getNewInstance()->crm()->contacts()->batchApi(),\n ],\n ];\n\n if (! isset($configurations[$objectType])) {\n throw new \\InvalidArgumentException(\"Unsupported object type: {$objectType}\");\n }\n\n return $configurations[$objectType];\n }\n\n private function prepareBatchRequest(array $batchConfig, array $crmIds, array $fields): object\n {\n $batchReadRequest = $batchConfig['batchReadRequest'];\n $inputClass = $batchConfig['inputClass'];\n\n $inputs = array_map(function ($crmId) use ($inputClass) {\n $input = new $inputClass();\n $input->setId($crmId);\n\n return $input;\n }, $crmIds);\n\n $batchReadRequest->setInputs($inputs);\n $batchReadRequest->setProperties($fields);\n\n return $batchReadRequest;\n }\n\n private function validateApiResponse($response, string $objectType): void\n {\n if (! $response) {\n throw new CrmException(\"HubSpot API returned null response for {$objectType} batch read\");\n }\n }\n\n private function processApiResults($response): array\n {\n $results = [];\n $responseResults = $response->getResults();\n\n if ($responseResults) {\n foreach ($responseResults as $object) {\n if ($object && $object->getId()) {\n $results[$object->getId()] = [\n 'id' => $object->getId(),\n 'properties' => $object->getProperties() ?: [],\n ];\n }\n }\n }\n\n return $results;\n }\n\n private function logBatchResults(string $objectType, array $crmIds, array $results): void\n {\n $this->log->info(\"[HubSpot] Batch fetched {$objectType}\", [\n 'requested_count' => count($crmIds),\n 'returned_count' => count($results),\n 'crm_ids' => $crmIds,\n ]);\n }\n\n private function handleBatchError(\\Throwable $e, string $objectType, array $crmIds): void\n {\n $errorMessage = $e->getMessage() ?: 'Unknown error';\n $errorTrace = $e->getTraceAsString() ?: 'No trace available';\n\n $this->log->error(\"[HubSpot] Failed to batch fetch {$objectType}\", [\n 'crm_ids' => $crmIds,\n 'error' => $errorMessage,\n 'trace' => $errorTrace,\n ]);\n\n throw new CrmException(\"Failed to batch fetch {$objectType}: \" . $errorMessage);\n }\n\n /**\n * Batch read multiple opportunities by their CRM IDs\n *\n * @param array<string> $crmIds Array of HubSpot deal IDs (max 100)\n * @param array<string> $fields Array of property names to fetch\n *\n * @return array<string, array> Array keyed by CRM ID with opportunity data\n */\n public function getOpportunitiesByIds(array $crmIds, array $fields): array\n {\n return $this->batchReadObjects('deals', $crmIds, $fields);\n }\n\n /**\n * Batch read multiple companies by their CRM IDs\n *\n * @param array<string> $crmIds Array of HubSpot company IDs (max 100)\n * @param array<string> $fields Array of property names to fetch\n *\n * @return array<string, array> Array keyed by CRM ID with company data\n */\n public function getCompaniesByIds(array $crmIds, array $fields): array\n {\n return $this->batchReadObjects('companies', $crmIds, $fields);\n }\n\n /**\n * Batch read multiple contacts by their CRM IDs\n *\n * @param array<string> $crmIds Array of HubSpot contact IDs (max 100)\n * @param array<string> $fields Array of property names to fetch\n *\n * @return array<string, array> Array keyed by CRM ID with contact data\n */\n public function getContactsByIds(array $crmIds, array $fields): array\n {\n return $this->batchReadObjects('contacts', $crmIds, $fields);\n }\n\n /**\n * @throws CompanyApiException\n * @throws CrmException\n */\n public function getAccountById(string $crmId, array $fields): array\n {\n try {\n $company = $this->getNewInstance()->crm()->companies()->basicApi()->getById(\n $crmId,\n implode(',', $fields),\n );\n } catch (CompanyApiException $e) {\n $this->log->info('[Hubspot] Failed to fetch account', [\n 'crm_id' => $crmId,\n 'reason' => $e->getMessage(),\n ]);\n\n throw $e;\n }\n\n if (! $company instanceof CompaniesWithAssociations) {\n throw new CrmException('Account not found');\n }\n\n return [\n 'id' => $company->getId(),\n 'properties' => $company->getProperties(),\n ];\n }\n\n /**\n * @throws ContactApiException\n * @throws CrmException\n */\n public function getContactById(string $crmId, array $fields): array\n {\n try {\n $contact = $this->getNewInstance()->crm()->contacts()->basicApi()->getById(\n $crmId,\n implode(',', $fields)\n );\n } catch (ContactApiException $e) {\n $this->log->info('[Hubspot] Failed to fetch contact', [\n 'crm_id' => $crmId,\n 'reason' => $e->getMessage(),\n ]);\n\n throw $e;\n }\n\n if (! $contact instanceof ContactsWithAssociations) {\n throw new CrmException('Contact not found');\n }\n\n return [\n 'id' => $contact->getId(),\n 'properties' => $contact->getProperties(),\n ];\n }\n\n /**\n * This is email search request that Hubspot offers as GET (more generous quota)\n */\n public function getContactByEmail(string $email, array $fields = []): array\n {\n try {\n $contact = $this->getNewInstance()->crm()->contacts()->basicApi()->getById(\n $email,\n implode(',', $fields),\n null,\n false,\n 'email'\n );\n\n return [\n 'id' => $contact->getId(),\n 'properties' => $contact->getProperties(),\n ];\n } catch (ContactApiException $e) {\n $this->log->info('[Hubspot] Failed to fetch contact', [\n 'email' => $email,\n 'reason' => $e->getMessage(),\n ]);\n\n return [];\n }\n }\n\n /**\n * @throws CrmException\n */\n public function fetchProperty(string $objectType, string $propertyId): Property\n {\n $result = $this->getNewInstance()->crm()->properties()->coreApi()->getByName($objectType, $propertyId);\n\n if (! $result instanceof Property) {\n $this->log->error('[Hubspot] Failed to fetch property', [\n 'object_type' => $objectType,\n 'property_id' => $propertyId,\n 'reason' => $result->getMessage(),\n ]);\n\n throw new CrmException('Failed to fetch property');\n }\n\n return $result;\n }\n\n /**\n * @return array<CrmFieldOption>\n */\n public function fetchPropertyOptions(string $objectType, string $propertyId): array\n {\n /** @var array<CrmFieldOption> */\n return $this->fetchProperty($objectType, $propertyId)->getOptions();\n }\n\n /**\n * @return array<array{id:string, label:string, deleted:bool}>\n */\n public function fetchCallDispositions(): array\n {\n /** @var Response $response */\n $response = $this->getInstance()->engagements()->getCallDispositions();\n\n /**\n * @var array<array{\n * id:string,\n * label:string,\n * deleted: bool\n * }>\n */\n return $response->toArray();\n }\n\n /**\n * @return array<CrmFieldOption>\n */\n public function fetchOpportunityPipelineStages(): array\n {\n $stages = [];\n $apiResponse = $this->getNewInstance()->crm()->pipelines()->pipelinesApi()->getAll('deals');\n\n if ($apiResponse instanceof Error) {\n $this->log->error('[Hubspot] Failed to fetch opportunity pipelines', [\n 'reason' => $apiResponse->getMessage(),\n ]);\n\n return [];\n }\n\n foreach ($apiResponse->getResults() as $pipeline) {\n $pipelineStages = array_map(\n static function (PipelineStage $stage) {\n return [\n 'id' => $stage->getId(),\n 'label' => $stage->getLabel(),\n ];\n },\n $pipeline->getStages()\n );\n\n $stages = array_merge($stages, $pipelineStages);\n }\n\n return $stages;\n }\n\n public function fetchOpportunityPipelines(): array\n {\n $pipelines = [];\n\n try {\n $apiResponse = $this->makeRequest('/crm/v3/pipelines/deals');\n } catch (\\Exception $e) {\n $this->log->info('[Hubspot] Failed to fetch opportunity pipelines', [\n 'reason' => $e->getMessage(),\n ]);\n\n return [];\n }\n\n $response = $apiResponse->toArray();\n\n foreach ($response['results'] as $pipeline) {\n $pipelines[] = [\n 'id' => $pipeline['id'],\n 'label' => $pipeline['label'],\n ];\n }\n\n return $pipelines;\n }\n\n /**\n * @return array<CrmFieldOption>\n */\n public function fetchMeetingOutcomeFieldOptions(Field $field): array\n {\n return $field->getCrmProviderId() === 'meetingOutcome'\n ? $this->fetchMeetingOutcomeTypes()\n : $this->fetchCallActivityTypes();\n }\n\n public function fetchMeetingOutcomeTypes(): array\n {\n return $this->extractMeetingTypeOptions(\n 'https://api.hubapi.com/crm/v3/properties/meeting/hs_meeting_outcome'\n );\n }\n\n public function fetchCallActivityTypes(): array\n {\n return $this->extractMeetingTypeOptions(\n 'https://api.hubapi.com/crm/v3/properties/call/hs_activity_type'\n );\n }\n\n private function extractMeetingTypeOptions(string $endpoint): array\n {\n /** @var Response $response */\n $response = $this->getInstance()\n ->getClient()\n ->request('GET', $endpoint);\n\n /**\n * @var array<array{\n * value: string,\n * label: string,\n * displayOrder: int\n * }> $optionData\n */\n $optionData = $response->toArray()['options'] ?? [];\n\n $options = [];\n foreach ($optionData as $item) {\n $options[] = [\n 'id' => $item['value'],\n 'value' => $item['value'],\n 'label' => $item['label'],\n 'display_order' => $item['displayOrder'],\n ];\n }\n\n return $options;\n }\n\n /**\n * @return array<CrmFieldOption>\n */\n public function fetchDispositionFieldOptions(): array\n {\n $options = [];\n\n $dispositions = $this->fetchCallDispositions();\n\n foreach ($dispositions as $disposition) {\n if ($disposition['deleted'] !== false) {\n continue;\n }\n\n $option['value'] = $disposition['id'];\n $option['id'] = $disposition['id'];\n $option['label'] = $disposition['label'];\n\n $options[] = $option;\n }\n\n return $options;\n }\n\n /**\n * @return array<CrmFieldOption>\n */\n public function fetchOpportunityFieldOptions(Field $field): array\n {\n if ($field->isStageField()) {\n return $this->fetchOpportunityPipelineStages();\n }\n\n if ($field->isPipelineField()) {\n return $this->fetchOpportunityPipelines();\n }\n\n return $this->fetchPropertyOptions('deals', $field->getCrmProviderId());\n }\n\n /**\n * @throws BadRequest\n * @throws HubspotException\n */\n public function makeRequest(string $endpoint, $method = 'GET', $payload = [], ?string $queryString = null)\n {\n $endpoint = self::BASE_URL . $endpoint;\n\n if ($method === 'GET') {\n return $this->getInstance()->getClient()?->request(\n method: $method,\n endpoint: $endpoint,\n query_string: $queryString\n );\n } else {\n return $this->getInstance()->getClient()->request($method, $endpoint, [\n 'json' => ($payload),\n ]);\n }\n }\n\n /**\n * @throws BadRequest\n * @throws HubspotException\n */\n public function createMeeting(array $payload): Response\n {\n $endpoint = '/crm/v3/objects/meetings';\n\n return $this->makeRequest($endpoint, 'POST', $payload);\n }\n\n /**\n * @throws BadRequest\n * @throws HubspotException\n */\n public function updateMeeting(string $meetingId, array $payload): Response\n {\n $endpoint = '/crm/v3/objects/meetings/' . $meetingId;\n\n return $this->makeRequest($endpoint, 'PATCH', $payload);\n }\n\n /**\n * @throws \\Exception\n */\n public function createNote(\n string $body,\n string $ownerId,\n int $timestamp,\n string $objectId,\n NoteObject $noteObject\n ): ?string {\n try {\n $noteInput = new SimplePublicObjectInput([\n 'properties' => [\n 'hs_note_body' => $body,\n 'hubspot_owner_id' => $ownerId,\n 'hs_timestamp' => $timestamp,\n ],\n ]);\n\n // Create note\n $note = $this->getNewInstance()->crm()->objects()->basicApi()->create('note', $noteInput);\n\n $this->getNewInstance()->crm()->objects()->associationsApi()->create(\n 'note',\n $note->getId(),\n $this->getNoteObject($noteObject),\n $objectId,\n $this->getNoteAssociationType($noteObject),\n );\n\n return $note->getId();\n } catch (\\Exception $e) {\n $this->log->error('[Hubspot] Failed to create note', [\n 'objectId' => $objectId,\n 'noteObject' => $noteObject->getObjectType(),\n 'reason' => $e->getMessage(),\n ]);\n\n \\Sentry::captureException($e);\n }\n\n return null;\n }\n\n public function updateEngagement(string $objectId, array $engagement, array $metadata): void\n {\n $this->getInstance()->engagements()->update($objectId, $engagement, $metadata);\n }\n\n public function getEngagementData(string $engagementId): array\n {\n $engagement = $this->getInstance()->engagements()->get($engagementId);\n\n return $engagement->toArray();\n }\n\n public function createEngagement(array $engagement, array $associations, array $metadata): Response\n {\n return $this->getInstance()\n ->engagements()\n ->create($engagement, $associations, $metadata);\n }\n\n public function isUnauthorizedException(\\Exception $e): bool\n {\n // Check for specific HubSpot API exception types first\n if ($e instanceof BadRequest) {\n // BadRequest can contain 401 status codes\n return $e->getCode() === 401;\n }\n\n // Check for HTTP client exceptions with status codes\n if ($e instanceof \\GuzzleHttp\\Exception\\RequestException && $e->hasResponse()) {\n $response = $e->getResponse();\n if ($response !== null) {\n return $response->getStatusCode() === 401;\n }\n }\n\n // Check for Guzzle HTTP exceptions\n if ($e instanceof \\GuzzleHttp\\Exception\\ClientException) {\n return $e->getCode() === 401;\n }\n\n // Fallback to string matching as last resort, but be more specific\n $message = strtolower($e->getMessage());\n\n return str_contains($message, '401 unauthorized') ||\n str_contains($message, 'http 401') ||\n str_contains($message, 'status code 401') ||\n (preg_match('/\\b401\\b/', $message) && str_contains($message, 'unauthorized'));\n }\n\n /**\n * Validates and refreshes the access token if needed before API requests.\n * This ensures long-running processes don't fail due to token expiration.\n *\n * @throws SocialAccountTokenInvalidException\n */\n public function ensureValidToken(): void\n {\n if ($this->oauthAccount === null) {\n return;\n }\n\n $newToken = $this->tokenManager->ensureValidToken($this->oauthAccount);\n if ($newToken !== null) {\n $this->accessToken = $newToken;\n }\n }\n\n public function getConfig()\n {\n return $this->config;\n }\n\n // returns only active (archived=false)\n public function getOwners(): array\n {\n return $this->getNewInstance()->crm()->owners()->getAll();\n }\n\n /**\n * @param bool $archived\n *\n * @return array<Owner>|[]\n */\n public function getOwnersArchived(bool $archived = true): array\n {\n $endpoint = '/crm/v3/owners';\n $queryParams = [\n 'archived' => $archived ? 'true' : 'false',\n ];\n $queryString = http_build_query($queryParams);\n\n $owners = [];\n\n try {\n $response = $this->makeRequest(endpoint: $endpoint, queryString: $queryString);\n $responseData = $response?->toArray();\n\n foreach ($responseData['results'] as $result) {\n try {\n $owners[] = Owner::create($result);\n } catch (Throwable $e) {\n $this->log->error('[HubSpot] Failed to process owner data', [\n 'result' => $result,\n 'error' => $e->getMessage(),\n ]);\n\n continue;\n }\n }\n } catch (Throwable $e) {\n $this->log->error('HubSpot] Failed to fetch owners', [\n 'archived' => $archived,\n 'error' => $e->getMessage(),\n ]);\n\n return [];\n }\n\n return $owners;\n }\n\n public function getMeeting(string $engagementId): ObjectWithAssociations\n {\n return $this->getNewInstance()->crm()->objects()->basicApi()\n ->getById('meeting', $engagementId, null, 'contact,company,deal');\n }\n\n public function deleteEngagement(string $engagementId): void\n {\n $this->getInstance()->engagements()->delete((int) $engagementId);\n }\n\n public function getAssociationsData(array $ids, string $fromObject, string $toObject): array\n {\n $associationData = [];\n $idChunks = array_chunk($ids, self::ASSOCIATIONS_BATCH_SIZE_LIMIT);\n\n foreach ($idChunks as $idChunk) {\n try {\n $batchInput = new \\HubSpot\\Client\\Crm\\Associations\\Model\\BatchInputPublicObjectId();\n $batchInput->setInputs(array_map(function ($id) {\n $publicObjectId = new \\HubSpot\\Client\\Crm\\Associations\\Model\\PublicObjectId();\n $publicObjectId->setId($id);\n\n return $publicObjectId;\n }, $idChunk));\n\n $associatedObjectsData = $this\n ->getNewInstance()\n ->crm()\n ->associations()\n ->batchApi()\n ->read($fromObject, $toObject, $batchInput);\n\n if ($associatedObjectsData instanceof \\HubSpot\\Client\\Crm\\Associations\\Model\\BatchResponsePublicAssociationMulti) {\n foreach ($associatedObjectsData->getResults() as $association) {\n $from = $association->getFrom()->getId();\n $toAssociations = $association->getTo();\n\n if (! empty($toAssociations)) {\n $associationData[$from] = array_map(function ($item) {\n return $item->getId();\n }, $toAssociations);\n }\n }\n }\n// } catch (RateLimitException $e) {\n// throw $e;\n } catch (\\Exception $e) {\n $this->log->error('[Hubspot] Failed to fetch associations', [\n 'from_object' => $fromObject,\n 'to_object' => $toObject,\n 'reason' => $e->getMessage(),\n ]);\n }\n }\n\n return $associationData;\n }\n\n /**\n * @throws \\Exception\n */\n private function getNoteAssociationType(NoteObject $noteObject): string\n {\n return match($noteObject) {\n NoteObject::Opportunity => 'note_to_deal',\n NoteObject::Lead, NoteObject::Contact => 'note_to_contact', // or 'note_to_lead' if your portal supports it\n NoteObject::Account => 'note_to_company',\n NoteObject::Call, NoteObject::Event => throw new \\Exception('Not supported'),\n };\n }\n\n /**\n * @throws \\Exception\n */\n private function getNoteObject(NoteObject $noteObject): string\n {\n return match($noteObject) {\n NoteObject::Opportunity => 'deal',\n NoteObject::Lead, NoteObject::Contact => 'contact',\n NoteObject::Account => 'company',\n NoteObject::Call, NoteObject::Event => throw new \\Exception('Not supported'),\n };\n }\n\n public function addAssociations(string $objectType, string $associationType, array $payload): Response\n {\n $endpoint = \"/crm/v4/associations/$objectType/$associationType/batch/create\";\n\n return $this->makeRequest($endpoint, 'POST', $payload);\n }\n\n public function removeAssociations(string $objectType, string $associationType, array $payload): Response\n {\n $endpoint = \"/crm/v4/associations/$objectType/$associationType/batch/archive\";\n\n return $this->makeRequest($endpoint, 'POST', $payload);\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":true,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"19","depth":4,"bounds":{"left":0.7124335,"top":0.10055866,"width":0.009640957,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.7237367,"top":0.09896249,"width":0.00731383,"height":0.018355945},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.73105055,"top":0.09896249,"width":0.006981383,"height":0.018355945},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"[2026-05-07 14:21:15] local.INFO: [Hubspot] DEBUG Getting headers {\n\"headers\":{\n\"Date\":[\"Thu,07 May 2026 14:21:15 GMT\"],\n \"Content-Type\":[\"application/json;charset=utf-8\"],\n \"Transfer-Encoding\":[\"chunked\"],\n \"Connection\":[\"keep-alive\"],\n \"CF-Ray\":[\"9f80deb8db60dc3a-SOF\"],\n \"CF-Cache-Status\":[\"DYNAMIC\"],\n \"Strict-Transport-Security\":[\"max-age=31536000; includeSubDomains; preload\"],\n \"Vary\":[\"origin,\n accept-encoding\"],\n \"access-control-allow-credentials\":[\"false\"],\n \"server-timing\":[\"hcid;desc=\\\"019e02d0-6fd8-7812-bdba-885b7ccb3ee3\\\",\n cfr;desc=\\\"9f80deb8e7c6dc3a-IAD\\\"\"],\n \"x-content-type-options\":[\"nosniff\"],\n \"x-hubspot-correlation-id\":[\"019e02d0-6fd8-7812-bdba-885b7ccb3ee3\"],\n \"Set-Cookie\":[\"__cf_bm=SIUrtdQgXVrik50pdqF6hZVYKhzTnQBidvMabeCtm0Y-1778163675-1.0.1.1-rI.ZggtDKxTge5zr8_2gbBfWMQQ.ufZEXDZyHz2mBUFdzdo2gTHEsOkXMSEShjK0hGYxNhUGM1ZoBpX7BcFZcHEjA7Cs_.SMUhUnd2nYjko; path=/; expires=Thu,\n 07-May-26 14:51:15 GMT; domain=.hubapi.com; HttpOnly; Secure; SameSite=None\"],\n \"Report-To\":[\"{\n\\\"endpoints\\\":[{\n\\\"url\\\":\\\"https:\\\\/\\\\/a.nel.cloudflare.com\\\\/report\\\\/v4?s=NYAlsVTP0fYm32qrSDjxYE4sd2RWRqiSp3wHsmdEgZlzoYdxI%2BIxVpHmsKn3O%2BKVA3mFIJ2m7YRECDGSM%2BW2IYTzo6FM4%2BdUIjURO8srzKSvJgZ%2BQ6R79arKQw3uHLlX\\\"}],\n\\\"group\\\":\\\"cf-nel\\\",\n\\\"max_age\\\":604800}\"],\n\"NEL\":[\"{\n\\\"success_fraction\\\":0.01,\n\\\"report_to\\\":\\\"cf-nel\\\",\n\\\"max_age\\\":604800}\"],\n\"Server\":[\"cloudflare\"]}} {\n\"correlation_id\":\"95236535-ec98-4541-b92a-adfa73b69eab\",\n\"trace_id\":\"c7ab8365-903f-46d4-9403-0e5b551e3545\"}","depth":4,"bounds":{"left":0.47672874,"top":0.09736632,"width":0.52327126,"height":0.8818835},"on_screen":true,"lines":[{"char_start":207,"char_count":30,"bounds":{"left":0.47672874,"top":0.0,"width":0.07513298,"height":0.014365523}},{"char_start":237,"char_count":36,"bounds":{"left":0.47672874,"top":0.0,"width":0.09075798,"height":0.014365523}},{"char_start":273,"char_count":32,"bounds":{"left":0.47672874,"top":0.0,"width":0.080119684,"height":0.014365523}},{"char_start":305,"char_count":79,"bounds":{"left":0.47672874,"top":0.0,"width":0.20212767,"height":0.014365523}},{"char_start":384,"char_count":18,"bounds":{"left":0.47672874,"top":0.0,"width":0.043882977,"height":0.014365523}},{"char_start":402,"char_count":21,"bounds":{"left":0.47672874,"top":0.0,"width":0.051861703,"height":0.014365523}},{"char_start":423,"char_count":48,"bounds":{"left":0.47672874,"top":0.008778931,"width":0.12167553,"height":0.014365523}},{"char_start":471,"char_count":72,"bounds":{"left":0.47672874,"top":0.026336791,"width":0.18384309,"height":0.014365523}},{"char_start":543,"char_count":40,"bounds":{"left":0.47672874,"top":0.043894652,"width":0.10106383,"height":0.014365523}},{"char_start":583,"char_count":41,"bounds":{"left":0.47672874,"top":0.061452515,"width":0.10372341,"height":0.014365523}},{"char_start":624,"char_count":72,"bounds":{"left":0.47672874,"top":0.079010375,"width":0.18384309,"height":0.014365523}},{"char_start":696,"char_count":219,"bounds":{"left":0.47672874,"top":0.096568234,"width":0.52327126,"height":0.014365523}},{"char_start":915,"char_count":83,"bounds":{"left":0.47672874,"top":0.11412609,"width":0.21243352,"height":0.014365523}},{"char_start":998,"char_count":20,"bounds":{"left":0.47672874,"top":0.13168396,"width":0.04920213,"height":0.014365523}},{"char_start":1018,"char_count":17,"bounds":{"left":0.47672874,"top":0.14924182,"width":0.041223403,"height":0.014365523}},{"char_start":1035,"char_count":203,"bounds":{"left":0.47672874,"top":0.16679968,"width":0.52327126,"height":0.014365523}},{"char_start":1238,"char_count":22,"bounds":{"left":0.47672874,"top":0.18435754,"width":0.05418883,"height":0.014365523}},{"char_start":1260,"char_count":23,"bounds":{"left":0.47672874,"top":0.2019154,"width":0.056848403,"height":0.014365523}},{"char_start":1283,"char_count":10,"bounds":{"left":0.47672874,"top":0.21947326,"width":0.023271276,"height":0.014365523}},{"char_start":1293,"char_count":27,"bounds":{"left":0.47672874,"top":0.23703113,"width":0.06715426,"height":0.014365523}},{"char_start":1320,"char_count":26,"bounds":{"left":0.47672874,"top":0.254589,"width":0.06482713,"height":0.014365523}},{"char_start":1346,"char_count":23,"bounds":{"left":0.47672874,"top":0.27214685,"width":0.056848403,"height":0.014365523}},{"char_start":1369,"char_count":28,"bounds":{"left":0.47672874,"top":0.2897047,"width":0.06981383,"height":0.014365523}},{"char_start":1397,"char_count":57,"bounds":{"left":0.47672874,"top":0.30726257,"width":0.14494681,"height":0.014365523}}],"value":"[2026-05-07 14:21:15] local.INFO: [Hubspot] DEBUG Getting headers {\n\"headers\":{\n\"Date\":[\"Thu,07 May 2026 14:21:15 GMT\"],\n \"Content-Type\":[\"application/json;charset=utf-8\"],\n \"Transfer-Encoding\":[\"chunked\"],\n \"Connection\":[\"keep-alive\"],\n \"CF-Ray\":[\"9f80deb8db60dc3a-SOF\"],\n \"CF-Cache-Status\":[\"DYNAMIC\"],\n \"Strict-Transport-Security\":[\"max-age=31536000; includeSubDomains; preload\"],\n \"Vary\":[\"origin,\n accept-encoding\"],\n \"access-control-allow-credentials\":[\"false\"],\n \"server-timing\":[\"hcid;desc=\\\"019e02d0-6fd8-7812-bdba-885b7ccb3ee3\\\",\n cfr;desc=\\\"9f80deb8e7c6dc3a-IAD\\\"\"],\n \"x-content-type-options\":[\"nosniff\"],\n \"x-hubspot-correlation-id\":[\"019e02d0-6fd8-7812-bdba-885b7ccb3ee3\"],\n \"Set-Cookie\":[\"__cf_bm=SIUrtdQgXVrik50pdqF6hZVYKhzTnQBidvMabeCtm0Y-1778163675-1.0.1.1-rI.ZggtDKxTge5zr8_2gbBfWMQQ.ufZEXDZyHz2mBUFdzdo2gTHEsOkXMSEShjK0hGYxNhUGM1ZoBpX7BcFZcHEjA7Cs_.SMUhUnd2nYjko; path=/; expires=Thu,\n 07-May-26 14:51:15 GMT; domain=.hubapi.com; HttpOnly; Secure; SameSite=None\"],\n \"Report-To\":[\"{\n\\\"endpoints\\\":[{\n\\\"url\\\":\\\"https:\\\\/\\\\/a.nel.cloudflare.com\\\\/report\\\\/v4?s=NYAlsVTP0fYm32qrSDjxYE4sd2RWRqiSp3wHsmdEgZlzoYdxI%2BIxVpHmsKn3O%2BKVA3mFIJ2m7YRECDGSM%2BW2IYTzo6FM4%2BdUIjURO8srzKSvJgZ%2BQ6R79arKQw3uHLlX\\\"}],\n\\\"group\\\":\\\"cf-nel\\\",\n\\\"max_age\\\":604800}\"],\n\"NEL\":[\"{\n\\\"success_fraction\\\":0.01,\n\\\"report_to\\\":\\\"cf-nel\\\",\n\\\"max_age\\\":604800}\"],\n\"Server\":[\"cloudflare\"]}} {\n\"correlation_id\":\"95236535-ec98-4541-b92a-adfa73b69eab\",\n\"trace_id\":\"c7ab8365-903f-46d4-9403-0e5b551e3545\"}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"bounds":{"left":0.011968086,"top":0.047885075,"width":0.024268618,"height":0.024740623},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
8569200697661330133
|
6378757150172121188
|
click
|
accessibility
|
NULL
|
Project: faVsco.js, menu
JY-20725-handle-HS-search Project: faVsco.js, menu
JY-20725-handle-HS-search-rate-limit, menu
Start Listening for PHP Debug Connections
HandleHubspotRateLimitTest
Run 'HandleHubspotRateLimitTest'
Debug 'HandleHubspotRateLimitTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
2
67
3
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Services\Crm\Hubspot;
use HubSpot\Client\Crm\Deals\ApiException as DealApiException;
use HubSpot\Client\Crm\Contacts\ApiException as ContactApiException;
use HubSpot\Client\Crm\Companies\ApiException as CompanyApiException;
use HubSpot\Client\Crm\Contacts\Model\SimplePublicObjectWithAssociations as ContactsWithAssociations;
use HubSpot\Client\Crm\Companies\Model\SimplePublicObjectWithAssociations as CompaniesWithAssociations;
use HubSpot\Client\Crm\Deals\Model\SimplePublicObjectWithAssociations as DealWithAssociations;
use HubSpot\Client\Crm\Objects\Model\SimplePublicObjectInput;
use HubSpot\Client\Crm\Objects\Model\SimplePublicObjectWithAssociations as ObjectWithAssociations;
use HubSpot\Client\Crm\Pipelines\Model\Error;
use HubSpot\Client\Crm\Pipelines\Model\PipelineStage;
use HubSpot\Client\Crm\Properties\Model\Property;
use HubSpot\Discovery\Discovery;
use Jiminny\Exceptions\CrmException;
use Jiminny\Exceptions\RateLimitException;
use Jiminny\Exceptions\SocialAccountTokenInvalidException;
use Jiminny\Jobs\Crm\NoteObject;
use Jiminny\Models\Crm\Field;
use Jiminny\Services\Crm\BaseClient;
use Jiminny\Services\Crm\Hubspot\DTO\Response\Owner;
use Jiminny\Services\SocialAccountService;
use SevenShores\Hubspot\Exceptions\BadRequest;
use SevenShores\Hubspot\Exceptions\HubspotException;
use SevenShores\Hubspot\Factory;
use SevenShores\Hubspot\Http\Response;
use Jiminny\Services\Crm\Hubspot\Pagination\HubspotPaginationService;
use Illuminate\Support\Facades\Redis;
use Throwable;
/**
* @phpstan-type CrmFieldOption array{id:string, label:string, value?:string}
*/
class Client extends BaseClient implements HubspotClientInterface
{
public const string MIN_API_VERSION = '2';
public const string BASE_URL = '[URL_WITH_CREDENTIALS] T
* @param callable(): T $apiCall
* @return T
*
* @throws RateLimitException
*/
private function executeRequest(callable $apiCall)
{
$cacheKey = $this->getRateLimitCacheKey();
$cachedRetryAfter = Redis::get($cacheKey);
if (is_string($cachedRetryAfter) && is_numeric($cachedRetryAfter)) {
throw new RateLimitException(
'Hubspot rate limit (cached circuit-breaker)',
(int) $cachedRetryAfter,
);
}
try {
return $apiCall();
} catch (Throwable $e) {
if ($this->isHubspotRateLimit($e)) {
$retryAfter = $this->parseRetryAfter($e);
Redis::setex($cacheKey, $retryAfter, (string) $retryAfter);
$this->log->warning('[Hubspot] Received 429 from API', [
'team_id' => $this->config->team_id,
'config_id' => $this->config->getId(),
'retry_after' => $retryAfter,
'message' => $this->parseMessage($e),
'reason' => $e->getMessage(),
]);
throw new RateLimitException('Hubspot returned 429', $retryAfter, $e);
}
throw $e;
}
}
private function getRateLimitCacheKey(): string
{
return sprintf('hubspot:ratelimit:portal:%d', $this->config->getId());
}
public function isHubspotRateLimit(Throwable $e): bool
{
if ($e instanceof BadRequest
|| $e instanceof DealApiException
|| $e instanceof ContactApiException
|| $e instanceof CompanyApiException
|| $e instanceof \GuzzleHttp\Exception\RequestException
) {
return (int) $e->getCode() === 429;
}
return false;
}
public function parseRetryAfter(Throwable $e): int
{
if (method_exists($e, 'getResponseHeaders')) {
$headers = $e->getResponseHeaders() ?: [];
$value = $headers['Retry-After'] ?? $headers['retry-after'] ?? null;
if (is_array($value)) {
$value = $value[0] ?? null;
}
if (is_numeric($value)) {
return (int) $value;
}
}
$message = strtolower($this->parseMessage($e));
if (str_contains($message, 'daily')) {
return 600;
}
if (str_contains($message, 'ten secondly')) {
return 10;
}
if (str_contains($message, 'secondly')) {
return 1;
}
$this->log->warning('[Hubspot] No retry-after header or known message, using default', [
'exception_class' => get_class($e),
'message' => $message,
]);
return 10;
}
public function parseMessage(Throwable $e): string
{
if (! method_exists($e, 'getResponseBody')) {
return '';
}
$body = $e->getResponseBody();
if (is_string($body)) {
$body = json_decode($body, true) ?? [];
}
if (! is_array($body)) {
return '';
}
return (string) ($body['message'] ?? '');
}
public function getMinimumApiVersion(): string
{
return self::MIN_API_VERSION;
}
public function getInstance(): Factory
{
return new Factory([
'key' => $this->accessToken,
'oauth2' => true,
'base_url' => $this->baseUrl,
]);
}
public function getNewInstance(): Discovery
{
return \HubSpot\Factory::createWithAccessToken($this->accessToken);
}
/**
* Secondly and daily limits for Hubspot API
*
* Product Tier: Free & Starter | Professional & Enterprise | API add-on (any tier)
* Burst: 100/10 seconds | 150/10 seconds | 200/10 seconds
* Daily: 250,000 | 500,000 | 1,000,000
*
* Official documentation states: The search endpoints are rate limited to five requests per second.
* Since with 5 RPS were still hitting secondly rate limits we lowered it to 4
*/
public function getPaginatedData(array $payload, string $type, int $offset = 0): array
{
$total = 0;
$lastId = null;
$rows = [];
foreach ($this->getPaginatedDataGenerator($payload, $type, $offset, $total, $lastId) as $row) {
$rows[] = $row;
}
return ['results' => $rows, 'total' => $total, 'last_record' => $lastId];
}
/**
* @throws HubspotException
* @throws SocialAccountTokenInvalidException
* @throws BadRequest
*/
public function getPaginatedDataGenerator(
array $payload,
string $type,
int $offset = 0,
int &$total = 0,
?string &$lastRecordId = null
): \Generator {
return $this->paginationService->getPaginatedDataGenerator(
$this,
$payload,
$type,
$offset,
$total,
$lastRecordId
);
}
/**
* Execute a search request against HubSpot CRM objects with rate limiting.
*
* @param string $objectType The object type ('deals', 'companies', 'contacts', 'calls')
* @param array<string, mixed> $payload The search payload with filters, sorts, properties, etc.
* @return array The search response with 'results', 'total', 'paging' keys
* @throws RateLimitException When rate limit is hit
* @throws HubspotException On API errors
*/
public function search(string $objectType, array $payload): array
{
$endpoint = self::BASE_URL . "/crm/v3/objects/{$objectType}/search";
return $this->executeRequest(function () use ($endpoint, $payload) {
$response = $this->getInstance()->getClient()->request('POST', $endpoint, ['json' => $payload]);
return $response->toArray();
});
}
/**
* @throws DealApiException
* @throws CrmException
*/
public function getOpportunityById(string $crmId, array $fields): array
{
try {
// $deal = $this->executeRequest(fn () => $this->getNewInstance()->crm()->deals()->basicApi()->getById(
$deal = $this->getNewInstance()->crm()->deals()->basicApi()->getById(
$crmId,
implode(',', $fields),
'companies,contacts'
);
} catch (DealApiException $e) {
$this->log->info('[Hubspot] Failed to fetch opportunity', [
'crm_id' => $crmId,
'reason' => $e->getMessage(),
]);
throw $e;
}
if (! $deal instanceof DealWithAssociations) {
throw new CrmException('Deal not found');
}
return [
'id' => $deal->getId(),
'properties' => $deal->getProperties(),
'associations' => $deal->getAssociations(),
];
}
/**
* Generic batch read method for HubSpot objects
*
* @param string $objectType The object type ('deals', 'companies', 'contacts')
* @param array<string> $crmIds Array of HubSpot object IDs (max 100)
* @param array<string> $fields Array of property names to fetch
*
* @return array<string, array> Array keyed by CRM ID with object data
*/
private function batchReadObjects(string $objectType, array $crmIds, array $fields): array
{
if (empty($crmIds)) {
return [];
}
$this->validateBatchSize($objectType, $crmIds);
$this->ensureValidToken();
try {
$batchConfig = $this->createBatchConfiguration($objectType);
$batchReadRequest = $this->prepareBatchRequest($batchConfig, $crmIds, $fields);
$response = $batchConfig['api']->read($batchReadRequest);
$this->validateApiResponse($response, $objectType);
$results = $this->processApiResults($response);
$this->logBatchResults($objectType, $crmIds, $results);
return $results;
} catch (\Throwable $e) {
$this->handleBatchError($e, $objectType, $crmIds);
}
}
private function validateBatchSize(string $objectType, array $crmIds): void
{
if (count($crmIds) > 100) {
throw new \InvalidArgumentException("Batch size cannot exceed 100 {$objectType}");
}
}
private function createBatchConfiguration(string $objectType): array
{
$configurations = [
'deals' => [
'batchReadRequest' => new \HubSpot\Client\Crm\Deals\Model\BatchReadInputSimplePublicObjectId(),
'inputClass' => \HubSpot\Client\Crm\Deals\Model\SimplePublicObjectId::class,
'api' => $this->getNewInstance()->crm()->deals()->batchApi(),
],
'companies' => [
'batchReadRequest' => new \HubSpot\Client\Crm\Companies\Model\BatchReadInputSimplePublicObjectId(),
'inputClass' => \HubSpot\Client\Crm\Companies\Model\SimplePublicObjectId::class,
'api' => $this->getNewInstance()->crm()->companies()->batchApi(),
],
'contacts' => [
'batchReadRequest' => new \HubSpot\Client\Crm\Contacts\Model\BatchReadInputSimplePublicObjectId(),
'inputClass' => \HubSpot\Client\Crm\Contacts\Model\SimplePublicObjectId::class,
'api' => $this->getNewInstance()->crm()->contacts()->batchApi(),
],
];
if (! isset($configurations[$objectType])) {
throw new \InvalidArgumentException("Unsupported object type: {$objectType}");
}
return $configurations[$objectType];
}
private function prepareBatchRequest(array $batchConfig, array $crmIds, array $fields): object
{
$batchReadRequest = $batchConfig['batchReadRequest'];
$inputClass = $batchConfig['inputClass'];
$inputs = array_map(function ($crmId) use ($inputClass) {
$input = new $inputClass();
$input->setId($crmId);
return $input;
}, $crmIds);
$batchReadRequest->setInputs($inputs);
$batchReadRequest->setProperties($fields);
return $batchReadRequest;
}
private function validateApiResponse($response, string $objectType): void
{
if (! $response) {
throw new CrmException("HubSpot API returned null response for {$objectType} batch read");
}
}
private function processApiResults($response): array
{
$results = [];
$responseResults = $response->getResults();
if ($responseResults) {
foreach ($responseResults as $object) {
if ($object && $object->getId()) {
$results[$object->getId()] = [
'id' => $object->getId(),
'properties' => $object->getProperties() ?: [],
];
}
}
}
return $results;
}
private function logBatchResults(string $objectType, array $crmIds, array $results): void
{
$this->log->info("[HubSpot] Batch fetched {$objectType}", [
'requested_count' => count($crmIds),
'returned_count' => count($results),
'crm_ids' => $crmIds,
]);
}
private function handleBatchError(\Throwable $e, string $objectType, array $crmIds): void
{
$errorMessage = $e->getMessage() ?: 'Unknown error';
$errorTrace = $e->getTraceAsString() ?: 'No trace available';
$this->log->error("[HubSpot] Failed to batch fetch {$objectType}", [
'crm_ids' => $crmIds,
'error' => $errorMessage,
'trace' => $errorTrace,
]);
throw new CrmException("Failed to batch fetch {$objectType}: " . $errorMessage);
}
/**
* Batch read multiple opportunities by their CRM IDs
*
* @param array<string> $crmIds Array of HubSpot deal IDs (max 100)
* @param array<string> $fields Array of property names to fetch
*
* @return array<string, array> Array keyed by CRM ID with opportunity data
*/
public function getOpportunitiesByIds(array $crmIds, array $fields): array
{
return $this->batchReadObjects('deals', $crmIds, $fields);
}
/**
* Batch read multiple companies by their CRM IDs
*
* @param array<string> $crmIds Array of HubSpot company IDs (max 100)
* @param array<string> $fields Array of property names to fetch
*
* @return array<string, array> Array keyed by CRM ID with company data
*/
public function getCompaniesByIds(array $crmIds, array $fields): array
{
return $this->batchReadObjects('companies', $crmIds, $fields);
}
/**
* Batch read multiple contacts by their CRM IDs
*
* @param array<string> $crmIds Array of HubSpot contact IDs (max 100)
* @param array<string> $fields Array of property names to fetch
*
* @return array<string, array> Array keyed by CRM ID with contact data
*/
public function getContactsByIds(array $crmIds, array $fields): array
{
return $this->batchReadObjects('contacts', $crmIds, $fields);
}
/**
* @throws CompanyApiException
* @throws CrmException
*/
public function getAccountById(string $crmId, array $fields): array
{
try {
$company = $this->getNewInstance()->crm()->companies()->basicApi()->getById(
$crmId,
implode(',', $fields),
);
} catch (CompanyApiException $e) {
$this->log->info('[Hubspot] Failed to fetch account', [
'crm_id' => $crmId,
'reason' => $e->getMessage(),
]);
throw $e;
}
if (! $company instanceof CompaniesWithAssociations) {
throw new CrmException('Account not found');
}
return [
'id' => $company->getId(),
'properties' => $company->getProperties(),
];
}
/**
* @throws ContactApiException
* @throws CrmException
*/
public function getContactById(string $crmId, array $fields): array
{
try {
$contact = $this->getNewInstance()->crm()->contacts()->basicApi()->getById(
$crmId,
implode(',', $fields)
);
} catch (ContactApiException $e) {
$this->log->info('[Hubspot] Failed to fetch contact', [
'crm_id' => $crmId,
'reason' => $e->getMessage(),
]);
throw $e;
}
if (! $contact instanceof ContactsWithAssociations) {
throw new CrmException('Contact not found');
}
return [
'id' => $contact->getId(),
'properties' => $contact->getProperties(),
];
}
/**
* This is email search request that Hubspot offers as GET (more generous quota)
*/
public function getContactByEmail(string $email, array $fields = []): array
{
try {
$contact = $this->getNewInstance()->crm()->contacts()->basicApi()->getById(
$email,
implode(',', $fields),
null,
false,
'email'
);
return [
'id' => $contact->getId(),
'properties' => $contact->getProperties(),
];
} catch (ContactApiException $e) {
$this->log->info('[Hubspot] Failed to fetch contact', [
'email' => $email,
'reason' => $e->getMessage(),
]);
return [];
}
}
/**
* @throws CrmException
*/
public function fetchProperty(string $objectType, string $propertyId): Property
{
$result = $this->getNewInstance()->crm()->properties()->coreApi()->getByName($objectType, $propertyId);
if (! $result instanceof Property) {
$this->log->error('[Hubspot] Failed to fetch property', [
'object_type' => $objectType,
'property_id' => $propertyId,
'reason' => $result->getMessage(),
]);
throw new CrmException('Failed to fetch property');
}
return $result;
}
/**
* @return array<CrmFieldOption>
*/
public function fetchPropertyOptions(string $objectType, string $propertyId): array
{
/** @var array<CrmFieldOption> */
return $this->fetchProperty($objectType, $propertyId)->getOptions();
}
/**
* @return array<array{id:string, label:string, deleted:bool}>
*/
public function fetchCallDispositions(): array
{
/** @var Response $response */
$response = $this->getInstance()->engagements()->getCallDispositions();
/**
* @var array<array{
* id:string,
* label:string,
* deleted: bool
* }>
*/
return $response->toArray();
}
/**
* @return array<CrmFieldOption>
*/
public function fetchOpportunityPipelineStages(): array
{
$stages = [];
$apiResponse = $this->getNewInstance()->crm()->pipelines()->pipelinesApi()->getAll('deals');
if ($apiResponse instanceof Error) {
$this->log->error('[Hubspot] Failed to fetch opportunity pipelines', [
'reason' => $apiResponse->getMessage(),
]);
return [];
}
foreach ($apiResponse->getResults() as $pipeline) {
$pipelineStages = array_map(
static function (PipelineStage $stage) {
return [
'id' => $stage->getId(),
'label' => $stage->getLabel(),
];
},
$pipeline->getStages()
);
$stages = array_merge($stages, $pipelineStages);
}
return $stages;
}
public function fetchOpportunityPipelines(): array
{
$pipelines = [];
try {
$apiResponse = $this->makeRequest('/crm/v3/pipelines/deals');
} catch (\Exception $e) {
$this->log->info('[Hubspot] Failed to fetch opportunity pipelines', [
'reason' => $e->getMessage(),
]);
return [];
}
$response = $apiResponse->toArray();
foreach ($response['results'] as $pipeline) {
$pipelines[] = [
'id' => $pipeline['id'],
'label' => $pipeline['label'],
];
}
return $pipelines;
}
/**
* @return array<CrmFieldOption>
*/
public function fetchMeetingOutcomeFieldOptions(Field $field): array
{
return $field->getCrmProviderId() === 'meetingOutcome'
? $this->fetchMeetingOutcomeTypes()
: $this->fetchCallActivityTypes();
}
public function fetchMeetingOutcomeTypes(): array
{
return $this->extractMeetingTypeOptions(
'[URL_WITH_CREDENTIALS] Response $response */
$response = $this->getInstance()
->getClient()
->request('GET', $endpoint);
/**
* @var array<array{
* value: string,
* label: string,
* displayOrder: int
* }> $optionData
*/
$optionData = $response->toArray()['options'] ?? [];
$options = [];
foreach ($optionData as $item) {
$options[] = [
'id' => $item['value'],
'value' => $item['value'],
'label' => $item['label'],
'display_order' => $item['displayOrder'],
];
}
return $options;
}
/**
* @return array<CrmFieldOption>
*/
public function fetchDispositionFieldOptions(): array
{
$options = [];
$dispositions = $this->fetchCallDispositions();
foreach ($dispositions as $disposition) {
if ($disposition['deleted'] !== false) {
continue;
}
$option['value'] = $disposition['id'];
$option['id'] = $disposition['id'];
$option['label'] = $disposition['label'];
$options[] = $option;
}
return $options;
}
/**
* @return array<CrmFieldOption>
*/
public function fetchOpportunityFieldOptions(Field $field): array
{
if ($field->isStageField()) {
return $this->fetchOpportunityPipelineStages();
}
if ($field->isPipelineField()) {
return $this->fetchOpportunityPipelines();
}
return $this->fetchPropertyOptions('deals', $field->getCrmProviderId());
}
/**
* @throws BadRequest
* @throws HubspotException
*/
public function makeRequest(string $endpoint, $method = 'GET', $payload = [], ?string $queryString = null)
{
$endpoint = self::BASE_URL . $endpoint;
if ($method === 'GET') {
return $this->getInstance()->getClient()?->request(
method: $method,
endpoint: $endpoint,
query_string: $queryString
);
} else {
return $this->getInstance()->getClient()->request($method, $endpoint, [
'json' => ($payload),
]);
}
}
/**
* @throws BadRequest
* @throws HubspotException
*/
public function createMeeting(array $payload): Response
{
$endpoint = '/crm/v3/objects/meetings';
return $this->makeRequest($endpoint, 'POST', $payload);
}
/**
* @throws BadRequest
* @throws HubspotException
*/
public function updateMeeting(string $meetingId, array $payload): Response
{
$endpoint = '/crm/v3/objects/meetings/' . $meetingId;
return $this->makeRequest($endpoint, 'PATCH', $payload);
}
/**
* @throws \Exception
*/
public function createNote(
string $body,
string $ownerId,
int $timestamp,
string $objectId,
NoteObject $noteObject
): ?string {
try {
$noteInput = new SimplePublicObjectInput([
'properties' => [
'hs_note_body' => $body,
'hubspot_owner_id' => $ownerId,
'hs_timestamp' => $timestamp,
],
]);
// Create note
$note = $this->getNewInstance()->crm()->objects()->basicApi()->create('note', $noteInput);
$this->getNewInstance()->crm()->objects()->associationsApi()->create(
'note',
$note->getId(),
$this->getNoteObject($noteObject),
$objectId,
$this->getNoteAssociationType($noteObject),
);
return $note->getId();
} catch (\Exception $e) {
$this->log->error('[Hubspot] Failed to create note', [
'objectId' => $objectId,
'noteObject' => $noteObject->getObjectType(),
'reason' => $e->getMessage(),
]);
\Sentry::captureException($e);
}
return null;
}
public function updateEngagement(string $objectId, array $engagement, array $metadata): void
{
$this->getInstance()->engagements()->update($objectId, $engagement, $metadata);
}
public function getEngagementData(string $engagementId): array
{
$engagement = $this->getInstance()->engagements()->get($engagementId);
return $engagement->toArray();
}
public function createEngagement(array $engagement, array $associations, array $metadata): Response
{
return $this->getInstance()
->engagements()
->create($engagement, $associations, $metadata);
}
public function isUnauthorizedException(\Exception $e): bool
{
// Check for specific HubSpot API exception types first
if ($e instanceof BadRequest) {
// BadRequest can contain 401 status codes
return $e->getCode() === 401;
}
// Check for HTTP client exceptions with status codes
if ($e instanceof \GuzzleHttp\Exception\RequestException && $e->hasResponse()) {
$response = $e->getResponse();
if ($response !== null) {
return $response->getStatusCode() === 401;
}
}
// Check for Guzzle HTTP exceptions
if ($e instanceof \GuzzleHttp\Exception\ClientException) {
return $e->getCode() === 401;
}
// Fallback to string matching as last resort, but be more specific
$message = strtolower($e->getMessage());
return str_contains($message, '401 unauthorized') ||
str_contains($message, 'http 401') ||
str_contains($message, 'status code 401') ||
(preg_match('/\b401\b/', $message) && str_contains($message, 'unauthorized'));
}
/**
* Validates and refreshes the access token if needed before API requests.
* This ensures long-running processes don't fail due to token expiration.
*
* @throws SocialAccountTokenInvalidException
*/
public function ensureValidToken(): void
{
if ($this->oauthAccount === null) {
return;
}
$newToken = $this->tokenManager->ensureValidToken($this->oauthAccount);
if ($newToken !== null) {
$this->accessToken = $newToken;
}
}
public function getConfig()
{
return $this->config;
}
// returns only active (archived=false)
public function getOwners(): array
{
return $this->getNewInstance()->crm()->owners()->getAll();
}
/**
* @param bool $archived
*
* @return array<Owner>|[]
*/
public function getOwnersArchived(bool $archived = true): array
{
$endpoint = '/crm/v3/owners';
$queryParams = [
'archived' => $archived ? 'true' : 'false',
];
$queryString = http_build_query($queryParams);
$owners = [];
try {
$response = $this->makeRequest(endpoint: $endpoint, queryString: $queryString);
$responseData = $response?->toArray();
foreach ($responseData['results'] as $result) {
try {
$owners[] = Owner::create($result);
} catch (Throwable $e) {
$this->log->error('[HubSpot] Failed to process owner data', [
'result' => $result,
'error' => $e->getMessage(),
]);
continue;
}
}
} catch (Throwable $e) {
$this->log->error('HubSpot] Failed to fetch owners', [
'archived' => $archived,
'error' => $e->getMessage(),
]);
return [];
}
return $owners;
}
public function getMeeting(string $engagementId): ObjectWithAssociations
{
return $this->getNewInstance()->crm()->objects()->basicApi()
->getById('meeting', $engagementId, null, 'contact,company,deal');
}
public function deleteEngagement(string $engagementId): void
{
$this->getInstance()->engagements()->delete((int) $engagementId);
}
public function getAssociationsData(array $ids, string $fromObject, string $toObject): array
{
$associationData = [];
$idChunks = array_chunk($ids, self::ASSOCIATIONS_BATCH_SIZE_LIMIT);
foreach ($idChunks as $idChunk) {
try {
$batchInput = new \HubSpot\Client\Crm\Associations\Model\BatchInputPublicObjectId();
$batchInput->setInputs(array_map(function ($id) {
$publicObjectId = new \HubSpot\Client\Crm\Associations\Model\PublicObjectId();
$publicObjectId->setId($id);
return $publicObjectId;
}, $idChunk));
$associatedObjectsData = $this
->getNewInstance()
->crm()
->associations()
->batchApi()
->read($fromObject, $toObject, $batchInput);
if ($associatedObjectsData instanceof \HubSpot\Client\Crm\Associations\Model\BatchResponsePublicAssociationMulti) {
foreach ($associatedObjectsData->getResults() as $association) {
$from = $association->getFrom()->getId();
$toAssociations = $association->getTo();
if (! empty($toAssociations)) {
$associationData[$from] = array_map(function ($item) {
return $item->getId();
}, $toAssociations);
}
}
}
// } catch (RateLimitException $e) {
// throw $e;
} catch (\Exception $e) {
$this->log->error('[Hubspot] Failed to fetch associations', [
'from_object' => $fromObject,
'to_object' => $toObject,
'reason' => $e->getMessage(),
]);
}
}
return $associationData;
}
/**
* @throws \Exception
*/
private function getNoteAssociationType(NoteObject $noteObject): string
{
return match($noteObject) {
NoteObject::Opportunity => 'note_to_deal',
NoteObject::Lead, NoteObject::Contact => 'note_to_contact', // or 'note_to_lead' if your portal supports it
NoteObject::Account => 'note_to_company',
NoteObject::Call, NoteObject::Event => throw new \Exception('Not supported'),
};
}
/**
* @throws \Exception
*/
private function getNoteObject(NoteObject $noteObject): string
{
return match($noteObject) {
NoteObject::Opportunity => 'deal',
NoteObject::Lead, NoteObject::Contact => 'contact',
NoteObject::Account => 'company',
NoteObject::Call, NoteObject::Event => throw new \Exception('Not supported'),
};
}
public function addAssociations(string $objectType, string $associationType, array $payload): Response
{
$endpoint = "/crm/v4/associations/$objectType/$associationType/batch/create";
return $this->makeRequest($endpoint, 'POST', $payload);
}
public function removeAssociations(string $objectType, string $associationType, array $payload): Response
{
$endpoint = "/crm/v4/associations/$objectType/$associationType/batch/archive";
return $this->makeRequest($endpoint, 'POST', $payload);
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
19
Previous Highlighted Error
Next Highlighted Error
[2026-05-07 14:21:15] local.INFO: [Hubspot] DEBUG Getting headers {
"headers":{
"Date":["Thu,07 May 2026 14:21:15 GMT"],
"Content-Type":["application/json;charset=utf-8"],
"Transfer-Encoding":["chunked"],
"Connection":["keep-alive"],
"CF-Ray":["9f80deb8db60dc3a-SOF"],
"CF-Cache-Status":["DYNAMIC"],
"Strict-Transport-Security":["max-age=31536000; includeSubDomains; preload"],
"Vary":["origin,
accept-encoding"],
"access-control-allow-credentials":["false"],
"server-timing":["hcid;desc=\"019e02d0-6fd8-7812-bdba-885b7ccb3ee3\",
cfr;desc=\"9f80deb8e7c6dc3a-IAD\""],
"x-content-type-options":["nosniff"],
"x-hubspot-correlation-id":["019e02d0-6fd8-7812-bdba-885b7ccb3ee3"],
"Set-Cookie":["__cf_bm=SIUrtdQgXVrik50pdqF6hZVYKhzTnQBidvMabeCtm0Y-1778163675-[IP_ADDRESS]-rI.ZggtDKxTge5zr8_2gbBfWMQQ.ufZEXDZyHz2mBUFdzdo2gTHEsOkXMSEShjK0hGYxNhUGM1ZoBpX7BcFZcHEjA7Cs_.SMUhUnd2nYjko; path=/; expires=Thu,
07-May-26 14:51:15 GMT; domain=.hubapi.com; HttpOnly; Secure; SameSite=None"],
"Report-To":["{
\"endpoints\":[{
\"url\":\"https:\\/\\/a.nel.cloudflare.com\\/report\\/v4?s=NYAlsVTP0fYm32qrSDjxYE4sd2RWRqiSp3wHsmdEgZlzoYdxI%2BIxVpHmsKn3O%2BKVA3mFIJ2m7YRECDGSM%2BW2IYTzo6FM4%2BdUIjURO8srzKSvJgZ%2BQ6R79arKQw3uHLlX\"}],
\"group\":\"cf-nel\",
\"max_age\":604800}"],
"NEL":["{
\"success_fraction\":0.01,
\"report_to\":\"cf-nel\",
\"max_age\":604800}"],
"Server":["cloudflare"]}} {
"correlation_id":"95236535-ec98-4541-b92a-adfa73b69eab",
"trace_id":"c7ab8365-903f-46d4-9403-0e5b551e3545"}
Project
Project...
|
18196
|
NULL
|
NULL
|
NULL
|
|
18199
|
790
|
6
|
2026-05-11T11:03:53.958213+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-11/1778 /Users/lukas/.screenpipe/data/data/2026-05-11/1778497433958_m1.jpg...
|
PhpStorm
|
faVsco.js – Client.php
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
SlackFileEditViewGoHistoryWindowHelpDOCKERO ₴1DEV SlackFileEditViewGoHistoryWindowHelpDOCKERO ₴1DEV (docker)$2APP (-zsh)883viewsjiminny-worker-processing-2:jiminny-worker-processing-2_00: stoppedjiminny-worker-processing-3:jiminny-worker-processing-3_00: stoppedjiminny-worker-processing-4: jiminny-worker-processing-4_00: stoppedjiminny-worker-processing-5:jiminny-worker-processing-5_00:jiminny-worker-processing-delayed: jiminny-worker-processing-delayed_00: stoppedworker-analytics:worker-analytics_00: stoppedworker-conferences:worker-conferences_00: stoppedworker-crm-update:worker-crm-update_00: stoppedworker-download:worker-download_00: stoppedworker-emails:worker-emails_00:worker-nudges:worker-nudges_00: stoppedworker:worker_00: stoppedworker-audio:worker-audio_00: stoppedworker-calendar:worker-calendar_00: stoppedworker-crm-sync:worker-crm-sync_00: stoppedartisan-schedule:artisan-schedule_00: stoppedworker-es-update:worker-es-update_00: stoppedjiminny-worker-processing-1:jiminny-worker-processing-1_00: stoppedartisan-schedule:artisan-schedule_00: startedjiminny-worker-processing-1:jiminny-worker-processing-1_00: startedjiminny-worker-processing-2:jiminny-worker-processing-2_00: startedjiminny-worker-processing-3:jiminny-worker-processing-3_00: startedjiminny-worker-processing-4:jiminny-worker-processing-4_00: startedjiminny-worker-processing-5:jiminny-worker-processing-5_00:jiminny-worker-processing-delayed: jiminny-worker-processing-delayed_00: startedworker:worker_00: startedworker-analytics:worker-analytics_00: startedworker-audio:worker-audio_00: startedworker-calendar:worker-calendar_00: startedworker-conferences:worker-conferences_00: startedworker-crm-sync:worker-crm-sync_00: startedworker-crm-update:worker-crm-update_00: startedworker-download:worker-download_00: startedworker-emails:worker-emails_00: startedworker-es-update:worker-es-update_00: startedworker-nudges:worker-nudges_00: startedroot@docker_lamp_1:/home/jiminny#php artisan jiminny:debugDispatching 100 MatchActivityCrmData jobs (portal=2)Done. Watch logs and run jiminny:debug observeRateLimit to inspect cache state.root@docker_lamp_1:/home/jiminny# php artisan jiminny:debugDispatching 100 MatchActivityCrmData jobs (portal=2)Done. Watch logs and run jiminny:debug observeRateLimit to inspect cache state.root@docker_lamp_1:/home/jiminny# ]DEV (d)-zsh• .HomeDMsActivityLater...liholg Preparation for Refi... in 57 m100% <78• Mon 11 May14:03:53→Describe what you are looking forJiminny ...# contusion-clinic# curiosity_lab# engineering# general# jiminny-bg# platform-tickets# product_launches# random# releases# sofia-office# support# thank-yous# the_people_of jimi...ToastHomeMessages^ Direct messages®. Aneliya Angelova®. Galya Dimitrova I& Petko Kashinski&. Stefka StoyanovaVasil Vasilev&. Nikolay IvanovAneliya Angelova, ...Stoyan Tanev®. VesE Lukas Kovalik y... 0AboutiminnyFriday, May 8th ~Added by Toast for GitHubToday ~Toast APP10:00 AMReviewapp#12059 Jy 20820 es reindexstream model hydration2 days old • 12 files changed •@Vasil VasilevAdded by Toast for GitHubResolve Conflictsapp#11443 Test hublets latency5 months old • 20 files changed#11327 JY-19501 webhookbased opportunity syncShow moreAdded by Toast for GitHubNeeds Loveapp#12024 JY-20773 fix user pilottracking ofr automated reportgenerated12 days old • 1 file changedE:: AppsMessage ToastJira CloudToast+AaConala CalaNew...
|
NULL
|
-9025442290629508027
|
NULL
|
click
|
ocr
|
NULL
|
SlackFileEditViewGoHistoryWindowHelpDOCKERO ₴1DEV SlackFileEditViewGoHistoryWindowHelpDOCKERO ₴1DEV (docker)$2APP (-zsh)883viewsjiminny-worker-processing-2:jiminny-worker-processing-2_00: stoppedjiminny-worker-processing-3:jiminny-worker-processing-3_00: stoppedjiminny-worker-processing-4: jiminny-worker-processing-4_00: stoppedjiminny-worker-processing-5:jiminny-worker-processing-5_00:jiminny-worker-processing-delayed: jiminny-worker-processing-delayed_00: stoppedworker-analytics:worker-analytics_00: stoppedworker-conferences:worker-conferences_00: stoppedworker-crm-update:worker-crm-update_00: stoppedworker-download:worker-download_00: stoppedworker-emails:worker-emails_00:worker-nudges:worker-nudges_00: stoppedworker:worker_00: stoppedworker-audio:worker-audio_00: stoppedworker-calendar:worker-calendar_00: stoppedworker-crm-sync:worker-crm-sync_00: stoppedartisan-schedule:artisan-schedule_00: stoppedworker-es-update:worker-es-update_00: stoppedjiminny-worker-processing-1:jiminny-worker-processing-1_00: stoppedartisan-schedule:artisan-schedule_00: startedjiminny-worker-processing-1:jiminny-worker-processing-1_00: startedjiminny-worker-processing-2:jiminny-worker-processing-2_00: startedjiminny-worker-processing-3:jiminny-worker-processing-3_00: startedjiminny-worker-processing-4:jiminny-worker-processing-4_00: startedjiminny-worker-processing-5:jiminny-worker-processing-5_00:jiminny-worker-processing-delayed: jiminny-worker-processing-delayed_00: startedworker:worker_00: startedworker-analytics:worker-analytics_00: startedworker-audio:worker-audio_00: startedworker-calendar:worker-calendar_00: startedworker-conferences:worker-conferences_00: startedworker-crm-sync:worker-crm-sync_00: startedworker-crm-update:worker-crm-update_00: startedworker-download:worker-download_00: startedworker-emails:worker-emails_00: startedworker-es-update:worker-es-update_00: startedworker-nudges:worker-nudges_00: startedroot@docker_lamp_1:/home/jiminny#php artisan jiminny:debugDispatching 100 MatchActivityCrmData jobs (portal=2)Done. Watch logs and run jiminny:debug observeRateLimit to inspect cache state.root@docker_lamp_1:/home/jiminny# php artisan jiminny:debugDispatching 100 MatchActivityCrmData jobs (portal=2)Done. Watch logs and run jiminny:debug observeRateLimit to inspect cache state.root@docker_lamp_1:/home/jiminny# ]DEV (d)-zsh• .HomeDMsActivityLater...liholg Preparation for Refi... in 57 m100% <78• Mon 11 May14:03:53→Describe what you are looking forJiminny ...# contusion-clinic# curiosity_lab# engineering# general# jiminny-bg# platform-tickets# product_launches# random# releases# sofia-office# support# thank-yous# the_people_of jimi...ToastHomeMessages^ Direct messages®. Aneliya Angelova®. Galya Dimitrova I& Petko Kashinski&. Stefka StoyanovaVasil Vasilev&. Nikolay IvanovAneliya Angelova, ...Stoyan Tanev®. VesE Lukas Kovalik y... 0AboutiminnyFriday, May 8th ~Added by Toast for GitHubToday ~Toast APP10:00 AMReviewapp#12059 Jy 20820 es reindexstream model hydration2 days old • 12 files changed •@Vasil VasilevAdded by Toast for GitHubResolve Conflictsapp#11443 Test hublets latency5 months old • 20 files changed#11327 JY-19501 webhookbased opportunity syncShow moreAdded by Toast for GitHubNeeds Loveapp#12024 JY-20773 fix user pilottracking ofr automated reportgenerated12 days old • 1 file changedE:: AppsMessage ToastJira CloudToast+AaConala CalaNew...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
18200
|
791
|
5
|
2026-05-11T11:03:53.958511+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-11/1778 /Users/lukas/.screenpipe/data/data/2026-05-11/1778497433958_m2.jpg...
|
PhpStorm
|
faVsco.js – Client.php
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
PhostormINavicarecodeFV faVsco.jsroledey© TrackRec PhostormINavicarecodeFV faVsco.jsroledey© TrackRecordingFileSiz© TrackRecordingSizeEnT ValidateSmitProspectED AjReports0 Calendarn Conference0 Crm@ bullnornC Close_copper• J crmobiects_ DecorareAcuivily• DummyHelpersv HubspotAccountSvncStrate> Actionsa ContactsuncStratedFields• M lournal1 Metadatalv OpportunitySyncSt>MConcerns.(c) Hubsnotl actMoC HubspotLastMo(C) Hubsnotl actMo(C) Hubsnotl actMo(C) Hubsnotl actMo(C) HubsnotSinaleS© HubspotSyncStr© HubspotWebhoo~ M Padination© HubspotPaginat© PaginationConfi(C) PaqinationState> D ProspectSearchStr:› D Redisv D ServiceTraitsTOnoortunitvsvnc() SvncCrmEntitiesT SuncFieldstirait.T. WriteCrmTrait.o• M UtilsM WebhookC) BatchSvncCollectotl(c) RatchSvncRedisSe•) Client nho(C) ClocedDea|Stadecs@ DoalFieldsService rT SyncCrmEntitiesTrait.phpc) CachedCrmServiceDecorator.ongJiminnyDeougcommana.ongo RematchActivityonCrmObiectDetach.png( RateLimitExcep'© CrmObiectsResolver.phr() ProviderRateLimiter.phpclass Cuient extends BasecLient imolements Hubspotcuientinterfaceprivate function createBatchConfiguration(string SobjectType): array352'inputClass' => \HubSpot\Client\Crm\Contacts\Model\SimplePublic0bjectId::class.'api' => Sthis->getNewInstance@->crmO->contacts@->batchApiO.1t @ 1sset(SconfiqurationsSobnecttvoel0)InvalidArgumentException( message: "Unsupported obiect type: {SobiectType}"):return SconfiaurationsiSobiecttvoel:private function prepareBatchRequest(array $batchConfig, array $ermids, array $fields): object$batchReadRequest = $batchConfig['batchReadRequest'];SinnutClacs = Chatchtonfiartinnutclacctl•Sinputs = array_map(function (ScrmId) use (SinputClass) {Sinput = new $inputClassO:Sinnut->cottd(ScomTd)•return $input;}. ScrmIds):SbatchreadRequest->setinputss1nputs)rSbatchReadReguest->setProperties(Sfields):orivate function validateAoiResponse(Sresponse, strina Sobiectivoe): voidif ( Sresponse) 4throw new CrmException( message: "HubSpot API returned null response for {SobiectTvne} batch read").nnivate function nnocecsAniRecults(Snesnonse)• arnav= custom.loa X|A SF [jiminny@localhost]4 HS_local (jiminny@localhost]# console [PKoD)A console (EU]A console [STAGING][2026-05-07 14:21:15] local.INF0: [Hubspot] DEBUG Getting headers {w19A"Vace".L"Inu,or may 20z0 14.21.10 bMl"J,"CF-Ray":"9t80deb8dbo0dcsa-s0F"ar"Strict-Transport-Security":"max-aqe=31536000* 1ncLudesubbomains: preload").access-control-allow-credentials": "false"."server-timing": ["hcid;desc=|"019e02d0-6fd8-7812-bdba-885b7ccb3ee3)","x-hubspot-correlation-id":["019e02d0-6fd8-7812-bdba-885b7ccb3ee3"),"Set-Cookie":["__cf_bm=S1UrtdQgXVrik50pdqF6hZVYKhzTnQBidvMabeCtm0Y-1778163675-[IP_ADDRESS]-rI.ZggtDKxTg07-May-26 14:51:15 GMT; domain=.hubapi.com; Http0nly; Secure; SameSite=None"],"Renont-Tol."\"url\":"https:\\\/a.nel.cloudflare.com\\/report\\/v4?s=NYAlsVTP0fYm32qrSDjxYE4sd2RWRqiSp3wHsr\"group\":\"cf-nel\",\"max_age\":604800}"],"NEL": ["{"reporc to. "cr-nel,"max age":604800}"]"Server": ["cloudflare"]}} {"correlation 10":"95256555-ec98-4541-b9za-adta/sbo9eab",I"trace_1d":"C/aD8565-905t-4604-9405-0e5b551e5545'Preparation tor kell... In o/ m100% 2• Mon 11 May 14:03:53HandleHubspotRateLimitTest vCascadeHubspot Rate Limit Re+0 ..lets simplify it, just call it parseMessage and skip policy al togetherThought for 5s ›Read Client.php #L86-200b Client.php+17 -35Verity nothing broke:Command docker. taildocker execetests/Unit/Jobs/Middleware/HandleHubspotRateLimitTest-phptests/Unit/Services/Crm/Hubspot/ClientTest.php --no-coverage 2>&1 | tail -10pup92.20contzaeration: Phomes3.minnv/ohpunit.xml:D.:Time: 02:14.415. Memorv: 62.00 MB[PASSWORD_DOTS]DD..DD..D.D..DDDDD.DDD...65 / 80 (808)OK hut there were icsuec!All 80 tests still passSummaryRemoved narsePo Eicv entirelv. narseRetrvAfter now mans messade kevwords direct v to seconds without theintermediate policy conceptBefore: header a narsePolicy a nolicy constant a seconds After: header « narceMessage „ secondsChanges:• parsePolicy → deleted•narseMessade added: returns the raw messade strina from the response bodv (or .. )•parseRetryAfter does the keyword matching itself (daily → 600, ten secondly → 10, secondly → 1)• Log line in executeRequest now logs message instead of policy - more informative for debuggingThe keyword-match order is the same as before (daily before ten secondly before secondly ) to avoid substringcollision.TIaTa 1 111111 M 1 1.nl .Ask anvthina (84L)< CodeClaude Onus 4.7 MediumWN Windsurf Toams 271-24 UTF.8io 4 spaces...
|
NULL
|
-1456414680938433434
|
NULL
|
click
|
ocr
|
NULL
|
PhostormINavicarecodeFV faVsco.jsroledey© TrackRec PhostormINavicarecodeFV faVsco.jsroledey© TrackRecordingFileSiz© TrackRecordingSizeEnT ValidateSmitProspectED AjReports0 Calendarn Conference0 Crm@ bullnornC Close_copper• J crmobiects_ DecorareAcuivily• DummyHelpersv HubspotAccountSvncStrate> Actionsa ContactsuncStratedFields• M lournal1 Metadatalv OpportunitySyncSt>MConcerns.(c) Hubsnotl actMoC HubspotLastMo(C) Hubsnotl actMo(C) Hubsnotl actMo(C) Hubsnotl actMo(C) HubsnotSinaleS© HubspotSyncStr© HubspotWebhoo~ M Padination© HubspotPaginat© PaginationConfi(C) PaqinationState> D ProspectSearchStr:› D Redisv D ServiceTraitsTOnoortunitvsvnc() SvncCrmEntitiesT SuncFieldstirait.T. WriteCrmTrait.o• M UtilsM WebhookC) BatchSvncCollectotl(c) RatchSvncRedisSe•) Client nho(C) ClocedDea|Stadecs@ DoalFieldsService rT SyncCrmEntitiesTrait.phpc) CachedCrmServiceDecorator.ongJiminnyDeougcommana.ongo RematchActivityonCrmObiectDetach.png( RateLimitExcep'© CrmObiectsResolver.phr() ProviderRateLimiter.phpclass Cuient extends BasecLient imolements Hubspotcuientinterfaceprivate function createBatchConfiguration(string SobjectType): array352'inputClass' => \HubSpot\Client\Crm\Contacts\Model\SimplePublic0bjectId::class.'api' => Sthis->getNewInstance@->crmO->contacts@->batchApiO.1t @ 1sset(SconfiqurationsSobnecttvoel0)InvalidArgumentException( message: "Unsupported obiect type: {SobiectType}"):return SconfiaurationsiSobiecttvoel:private function prepareBatchRequest(array $batchConfig, array $ermids, array $fields): object$batchReadRequest = $batchConfig['batchReadRequest'];SinnutClacs = Chatchtonfiartinnutclacctl•Sinputs = array_map(function (ScrmId) use (SinputClass) {Sinput = new $inputClassO:Sinnut->cottd(ScomTd)•return $input;}. ScrmIds):SbatchreadRequest->setinputss1nputs)rSbatchReadReguest->setProperties(Sfields):orivate function validateAoiResponse(Sresponse, strina Sobiectivoe): voidif ( Sresponse) 4throw new CrmException( message: "HubSpot API returned null response for {SobiectTvne} batch read").nnivate function nnocecsAniRecults(Snesnonse)• arnav= custom.loa X|A SF [jiminny@localhost]4 HS_local (jiminny@localhost]# console [PKoD)A console (EU]A console [STAGING][2026-05-07 14:21:15] local.INF0: [Hubspot] DEBUG Getting headers {w19A"Vace".L"Inu,or may 20z0 14.21.10 bMl"J,"CF-Ray":"9t80deb8dbo0dcsa-s0F"ar"Strict-Transport-Security":"max-aqe=31536000* 1ncLudesubbomains: preload").access-control-allow-credentials": "false"."server-timing": ["hcid;desc=|"019e02d0-6fd8-7812-bdba-885b7ccb3ee3)","x-hubspot-correlation-id":["019e02d0-6fd8-7812-bdba-885b7ccb3ee3"),"Set-Cookie":["__cf_bm=S1UrtdQgXVrik50pdqF6hZVYKhzTnQBidvMabeCtm0Y-1778163675-[IP_ADDRESS]-rI.ZggtDKxTg07-May-26 14:51:15 GMT; domain=.hubapi.com; Http0nly; Secure; SameSite=None"],"Renont-Tol."\"url\":"https:\\\/a.nel.cloudflare.com\\/report\\/v4?s=NYAlsVTP0fYm32qrSDjxYE4sd2RWRqiSp3wHsr\"group\":\"cf-nel\",\"max_age\":604800}"],"NEL": ["{"reporc to. "cr-nel,"max age":604800}"]"Server": ["cloudflare"]}} {"correlation 10":"95256555-ec98-4541-b9za-adta/sbo9eab",I"trace_1d":"C/aD8565-905t-4604-9405-0e5b551e5545'Preparation tor kell... In o/ m100% 2• Mon 11 May 14:03:53HandleHubspotRateLimitTest vCascadeHubspot Rate Limit Re+0 ..lets simplify it, just call it parseMessage and skip policy al togetherThought for 5s ›Read Client.php #L86-200b Client.php+17 -35Verity nothing broke:Command docker. taildocker execetests/Unit/Jobs/Middleware/HandleHubspotRateLimitTest-phptests/Unit/Services/Crm/Hubspot/ClientTest.php --no-coverage 2>&1 | tail -10pup92.20contzaeration: Phomes3.minnv/ohpunit.xml:D.:Time: 02:14.415. Memorv: 62.00 MB[PASSWORD_DOTS]DD..DD..D.D..DDDDD.DDD...65 / 80 (808)OK hut there were icsuec!All 80 tests still passSummaryRemoved narsePo Eicv entirelv. narseRetrvAfter now mans messade kevwords direct v to seconds without theintermediate policy conceptBefore: header a narsePolicy a nolicy constant a seconds After: header « narceMessage „ secondsChanges:• parsePolicy → deleted•narseMessade added: returns the raw messade strina from the response bodv (or .. )•parseRetryAfter does the keyword matching itself (daily → 600, ten secondly → 10, secondly → 1)• Log line in executeRequest now logs message instead of policy - more informative for debuggingThe keyword-match order is the same as before (daily before ten secondly before secondly ) to avoid substringcollision.TIaTa 1 111111 M 1 1.nl .Ask anvthina (84L)< CodeClaude Onus 4.7 MediumWN Windsurf Toams 271-24 UTF.8io 4 spaces...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
18203
|
790
|
8
|
2026-05-11T11:04:00.914160+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-11/1778 /Users/lukas/.screenpipe/data/data/2026-05-11/1778497440914_m1.jpg...
|
PhpStorm
|
faVsco.js – custom.log
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
iTerm2ShellEditViewSessionScriptsProfilesWindowHel iTerm2ShellEditViewSessionScriptsProfilesWindowHelp•<DEV (docker)-zshlahlf Preparation for Refi... in 57 m-zsh85DOCKERO ₴1DEV (docker)APP (-zsh)#3jiminny-worker-processing-2:jiminny-worker-processing-2_00: stoppedjiminny-worker-processing-3:jiminny-worker-processing-3_00: stoppedjiminny-worker-processing-4:jiminny-worker-processing-4_00: stoppedjiminny-worker-processing-5: jiminny-worker-processing-5_00: stoppedjiminny-worker-processing-delayed: jiminny-worker-processing-delayed_00: stoppedworker-analytics:worker-analytics_00: stoppedworker-conferences:worker-conferences_00: stoppedworker-crm-update:worker-crm-update_00: stoppedworker-download:worker-download_00: stoppedworker-emails:worker-emails_00: stoppedworker-nudges:worker-nudges_00: stoppedworker:worker_00:stoppedworker-audio:worker-audio_00: stoppedworker-calendar:worker-calendar_00:stoppedworker-crm-sync:worker-crm-sync_00: stoppedartisan-schedule:artisan-schedule_00: stoppedworker-es-update:worker-es-update_00: stoppedjiminny-worker-processing-1:jiminny-worker-processing-1_00: stoppedartisan-schedule:artisan-schedule_00: startedjiminny-worker-processing-1:jiminny-worker-processing-1_00: startedjiminny-worker-processing-2:jiminny-worker-processing-2_00: startedjiminny-worker-processing-3:jiminny-worker-processing-3_00: startedjiminny-worker-processing-4:jiminny-worker-processing-4_00: startedjiminny-worker-processing-5:jiminny-worker-processing-5_00: startedjiminny-worker-processing-delayed: jiminny-worker-processing-delayed_00: startedworker:worker_00: startedworker-analytics:worker-analytics_00: startedworker-audio:worker-audio_00: startedworker-calendar:worker-calendar_00: startedworker-conferences:worker-conferences_00: startedworker-crm-sync:worker-crm-sync_00: startedworker-crm-update:worker-crm-update_00: startedworker-download:worker-download_00:startedworker-emails:worker-emails_00: startedworker-es-update:worker-es-update_00: startedworker-nudges:worker-nudges_00: startedroot@docker_lamp_1:/home/jiminny# php artisan jiminny: debugDispatching 100 MatchActivityCrmDatajobs (portal=2)Done. Watch logs and run jiminny:debug observeRateLimit to inspect cachestate.root@docker_lamp_1:/home/jiminny# php artisan jiminny: debugDispatching 100 MatchActivityCrmDatajobs (portal=2)Done.Watch logs and run jiminny:debug observeRateLimit toinspect cache state.root@docker_lamp_1:/home/jiminny# php artisan optimize:clear && supervisorctl restart allscreenpipe"100% C8 • Mon 11 May 14:04:00T81K 886-zsh*7DEV...
|
NULL
|
6104581256548199485
|
NULL
|
click
|
ocr
|
NULL
|
iTerm2ShellEditViewSessionScriptsProfilesWindowHel iTerm2ShellEditViewSessionScriptsProfilesWindowHelp•<DEV (docker)-zshlahlf Preparation for Refi... in 57 m-zsh85DOCKERO ₴1DEV (docker)APP (-zsh)#3jiminny-worker-processing-2:jiminny-worker-processing-2_00: stoppedjiminny-worker-processing-3:jiminny-worker-processing-3_00: stoppedjiminny-worker-processing-4:jiminny-worker-processing-4_00: stoppedjiminny-worker-processing-5: jiminny-worker-processing-5_00: stoppedjiminny-worker-processing-delayed: jiminny-worker-processing-delayed_00: stoppedworker-analytics:worker-analytics_00: stoppedworker-conferences:worker-conferences_00: stoppedworker-crm-update:worker-crm-update_00: stoppedworker-download:worker-download_00: stoppedworker-emails:worker-emails_00: stoppedworker-nudges:worker-nudges_00: stoppedworker:worker_00:stoppedworker-audio:worker-audio_00: stoppedworker-calendar:worker-calendar_00:stoppedworker-crm-sync:worker-crm-sync_00: stoppedartisan-schedule:artisan-schedule_00: stoppedworker-es-update:worker-es-update_00: stoppedjiminny-worker-processing-1:jiminny-worker-processing-1_00: stoppedartisan-schedule:artisan-schedule_00: startedjiminny-worker-processing-1:jiminny-worker-processing-1_00: startedjiminny-worker-processing-2:jiminny-worker-processing-2_00: startedjiminny-worker-processing-3:jiminny-worker-processing-3_00: startedjiminny-worker-processing-4:jiminny-worker-processing-4_00: startedjiminny-worker-processing-5:jiminny-worker-processing-5_00: startedjiminny-worker-processing-delayed: jiminny-worker-processing-delayed_00: startedworker:worker_00: startedworker-analytics:worker-analytics_00: startedworker-audio:worker-audio_00: startedworker-calendar:worker-calendar_00: startedworker-conferences:worker-conferences_00: startedworker-crm-sync:worker-crm-sync_00: startedworker-crm-update:worker-crm-update_00: startedworker-download:worker-download_00:startedworker-emails:worker-emails_00: startedworker-es-update:worker-es-update_00: startedworker-nudges:worker-nudges_00: startedroot@docker_lamp_1:/home/jiminny# php artisan jiminny: debugDispatching 100 MatchActivityCrmDatajobs (portal=2)Done. Watch logs and run jiminny:debug observeRateLimit to inspect cachestate.root@docker_lamp_1:/home/jiminny# php artisan jiminny: debugDispatching 100 MatchActivityCrmDatajobs (portal=2)Done.Watch logs and run jiminny:debug observeRateLimit toinspect cache state.root@docker_lamp_1:/home/jiminny# php artisan optimize:clear && supervisorctl restart allscreenpipe"100% C8 • Mon 11 May 14:04:00T81K 886-zsh*7DEV...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
18204
|
791
|
7
|
2026-05-11T11:04:00.933134+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-11/1778 /Users/lukas/.screenpipe/data/data/2026-05-11/1778497440933_m2.jpg...
|
PhpStorm
|
faVsco.js – custom.log
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
PnostormFV faVsco.jsroledey© TrackRecordingFileSiz PnostormFV faVsco.jsroledey© TrackRecordingFileSizC) TrackRecordingsizechT. ValidateSmitProspectEAjReports0 Calendarn Conference0 Crm@ bullnornC Close_copper• J crmobiects_ DecorareAcuivily• DummyHelpersv HubspotAccountSvncStrate> Actionsa ContactsuncStratedm Fields1 MetadatalvOpportunitvSvncSt>MConcerns.(c) Hubsnotl actMoC HubspotLastMo(C) Hubsnotl actMo(C) Hubsnotl actMo(C) Hubsnotl actMo(C) HubsnotSinaleS© HubspotSyncStr© HubspotWebhoo~ M Padination© HubspotPaginat© PaginationConfi(C) PaqinationState> D ProspectSearchStr:› D Redisv D ServiceTraitsTOnoortunitvsvnc() SvncCrmEntitiesT SuncFieldstirait.T. WriteCrmTrait.o• M UtilsM WebhookC) BatchSvncCollectotl(c) RatchSvncRedisSec) Client nhr(C) ClocedDea|Stadecs@ DoalFieldsService rT SyncCrmEntitiesTrait.phpc) CachedCrmServiceDecorator.ongJiminnyDeougcommana.ongo RematchActivityonCrmObiectDetach.png( RateLimitExcep'© CrmObiectsResolver.phr() ProviderRateLimiter.phpclass Cuient extends BasecLient imolements Hubspotcuientinterfaceprivate function createBatchConfiguration(string SobjectType): array352'inputClass' => \HubSpot\Client\Crm\Contacts\Model\SimplePublic0bjectId::class.'api' => Sthis->getNewInstance@->crmO->contacts@->batchApiO.1t @ 1sset(SconfiqurationsSobnecttvoel0)InvalidArgumentException( message: "Unsupported obiect type: {SobiectType}"):return SconfiaurationsiSobiecttvoel:private function prepareBatchRequest(array $batchConfig, array $ermids, array $fields): object$batchReadRequest = $batchConfig['batchReadRequest'];SinnutClacs = Chatchtonfiartinnutclacctl•Sinputs = array_map(function (ScrmId) use (SinputClass) {Sinput = new $inputClassO:Sinnut->cottd(ScomTd)•return $input;}. ScrmIds):SbatchreadRequest->setinputss1nputs)rSbatchReadReguest->setProperties(Sfields):orivate function validateAoiResponse(Sresponse, strina Sobiectivoe): voidif @ Sresponse)4throw new CrmException( message: "HubSpot APT returned null response for {SobiectTvnel batch read"):nnivate function nrocecsAniResults(Snesnanse)• arnavPreparation tor kell…. In o/l• моn 11 Mау 14:04.00HandleHubspotRateLimitTest v= custom.loa X|A SF [jiminny@localhost]4 HS_local (jiminny@localhost]# console [PKoD)CascadeA console (EU]A console [STAGING]Hubspot Rate Limit Re+0 ..[2026-05-07 14:21:15] local.INF0: [Hubspot] DEBUG Getting headers {w19Alets simplify it, just call it parseMessage and skip policy al together"Vace".L"Inu,or may 20z0 14.21.10 bMl"J,Thought for 5s ›Read Client.php #L86-200b Client.php+17 -35"CF-Ray":"9t80deb8dbo0dcsa-s0F"arVerity nothing broke:Command docker. tail"Strict-Transport-Security":"max-aqe=31536000* 1ncLudesubbomains: preload").access-control-allow-credentials": "false"."server-timing": ["hcid;desc=|"019e02d0-6fd8-7812-bdba-885b7ccb3ee3)",docketests/Unit/J0bs/Middleware/HandleHubspotRatel/mitTest-phptests/Unit/Services/Crm/Hubspot/ClientTest.php --no-coverage 2>&1 | tail -10cunfiguration: /home/3imlny/phpunit,xnl[PASSWORD_DOTS]DD..DD..D.D..DDDDD.DDD...D......65 / 80 (808)"x-hubspot-correlation-id":["019e02d0-6fd8-7812-bdba-885b7ccb3ee3"),"Set-Cookie":["__cf_bm=S1UrtdQgXVrik50pdqF6hZVYKhzTnQBidvMabeCtm0Y-1778163675-[IP_ADDRESS]-rI.ZggtDKxTg07-May-26 14:51:15 GMT; domain=.hubapi.com; Http0nly; Secure; SameSite=None"],"Renont-Tol."Time: 02:14.415. Memorv: 62.00 MBOK hut there were ¿csuec!All 80 tests still pass\"url\":"https:\\\/a.nel.cloudflare.com\\/report\\/v4?s=NYAlsVTP0fYm32qrSDjxYE4sd2RWRqiSp3wHsr\"group\":\"cf-nel\",\"max_age\":604800}"],"NEL": ["{SummaryRemoved narsePo Eicv entirelv. narseRetrvAfter now mans messade kevwords direct v to seconds without theintermediate policy conceptBefore: header a narsePolicy a nolicy constant a seconds After: header « narceMessage „ seconds"reporc to. "cr-nel,"max age":604800}"]"Server": ["cloudflare"]}} {Changes:• parsePolicy → deleted•narseMessade added: returns the raw messade strina from the response bodv (or .. )"correlation 10":"95256555-ec98-4541-b9za-adta/sboveab",•parseRetryAfter does the keyword matching itself (daily → 600, ten secondly → 10, secondly → 1)I"trace_1d":"C/aD8565-905t-4604-9405-0e5b551e5545'• Log line in executeRequest now logs message instead of policy - more informative for debuggingThe keyword-match order is the same as before (daily before ten secondly before secondly ) to avoid substringcollision.TIaTa 1 1111II MI 1 IL.nl .Ask anvthina (84L)< CodeClaude Onus 4.7 Medium271-24 UTE.Afo 4 spaces...
|
NULL
|
-1140554480591860892
|
NULL
|
click
|
ocr
|
NULL
|
PnostormFV faVsco.jsroledey© TrackRecordingFileSiz PnostormFV faVsco.jsroledey© TrackRecordingFileSizC) TrackRecordingsizechT. ValidateSmitProspectEAjReports0 Calendarn Conference0 Crm@ bullnornC Close_copper• J crmobiects_ DecorareAcuivily• DummyHelpersv HubspotAccountSvncStrate> Actionsa ContactsuncStratedm Fields1 MetadatalvOpportunitvSvncSt>MConcerns.(c) Hubsnotl actMoC HubspotLastMo(C) Hubsnotl actMo(C) Hubsnotl actMo(C) Hubsnotl actMo(C) HubsnotSinaleS© HubspotSyncStr© HubspotWebhoo~ M Padination© HubspotPaginat© PaginationConfi(C) PaqinationState> D ProspectSearchStr:› D Redisv D ServiceTraitsTOnoortunitvsvnc() SvncCrmEntitiesT SuncFieldstirait.T. WriteCrmTrait.o• M UtilsM WebhookC) BatchSvncCollectotl(c) RatchSvncRedisSec) Client nhr(C) ClocedDea|Stadecs@ DoalFieldsService rT SyncCrmEntitiesTrait.phpc) CachedCrmServiceDecorator.ongJiminnyDeougcommana.ongo RematchActivityonCrmObiectDetach.png( RateLimitExcep'© CrmObiectsResolver.phr() ProviderRateLimiter.phpclass Cuient extends BasecLient imolements Hubspotcuientinterfaceprivate function createBatchConfiguration(string SobjectType): array352'inputClass' => \HubSpot\Client\Crm\Contacts\Model\SimplePublic0bjectId::class.'api' => Sthis->getNewInstance@->crmO->contacts@->batchApiO.1t @ 1sset(SconfiqurationsSobnecttvoel0)InvalidArgumentException( message: "Unsupported obiect type: {SobiectType}"):return SconfiaurationsiSobiecttvoel:private function prepareBatchRequest(array $batchConfig, array $ermids, array $fields): object$batchReadRequest = $batchConfig['batchReadRequest'];SinnutClacs = Chatchtonfiartinnutclacctl•Sinputs = array_map(function (ScrmId) use (SinputClass) {Sinput = new $inputClassO:Sinnut->cottd(ScomTd)•return $input;}. ScrmIds):SbatchreadRequest->setinputss1nputs)rSbatchReadReguest->setProperties(Sfields):orivate function validateAoiResponse(Sresponse, strina Sobiectivoe): voidif @ Sresponse)4throw new CrmException( message: "HubSpot APT returned null response for {SobiectTvnel batch read"):nnivate function nrocecsAniResults(Snesnanse)• arnavPreparation tor kell…. In o/l• моn 11 Mау 14:04.00HandleHubspotRateLimitTest v= custom.loa X|A SF [jiminny@localhost]4 HS_local (jiminny@localhost]# console [PKoD)CascadeA console (EU]A console [STAGING]Hubspot Rate Limit Re+0 ..[2026-05-07 14:21:15] local.INF0: [Hubspot] DEBUG Getting headers {w19Alets simplify it, just call it parseMessage and skip policy al together"Vace".L"Inu,or may 20z0 14.21.10 bMl"J,Thought for 5s ›Read Client.php #L86-200b Client.php+17 -35"CF-Ray":"9t80deb8dbo0dcsa-s0F"arVerity nothing broke:Command docker. tail"Strict-Transport-Security":"max-aqe=31536000* 1ncLudesubbomains: preload").access-control-allow-credentials": "false"."server-timing": ["hcid;desc=|"019e02d0-6fd8-7812-bdba-885b7ccb3ee3)",docketests/Unit/J0bs/Middleware/HandleHubspotRatel/mitTest-phptests/Unit/Services/Crm/Hubspot/ClientTest.php --no-coverage 2>&1 | tail -10cunfiguration: /home/3imlny/phpunit,xnl[PASSWORD_DOTS]DD..DD..D.D..DDDDD.DDD...D......65 / 80 (808)"x-hubspot-correlation-id":["019e02d0-6fd8-7812-bdba-885b7ccb3ee3"),"Set-Cookie":["__cf_bm=S1UrtdQgXVrik50pdqF6hZVYKhzTnQBidvMabeCtm0Y-1778163675-[IP_ADDRESS]-rI.ZggtDKxTg07-May-26 14:51:15 GMT; domain=.hubapi.com; Http0nly; Secure; SameSite=None"],"Renont-Tol."Time: 02:14.415. Memorv: 62.00 MBOK hut there were ¿csuec!All 80 tests still pass\"url\":"https:\\\/a.nel.cloudflare.com\\/report\\/v4?s=NYAlsVTP0fYm32qrSDjxYE4sd2RWRqiSp3wHsr\"group\":\"cf-nel\",\"max_age\":604800}"],"NEL": ["{SummaryRemoved narsePo Eicv entirelv. narseRetrvAfter now mans messade kevwords direct v to seconds without theintermediate policy conceptBefore: header a narsePolicy a nolicy constant a seconds After: header « narceMessage „ seconds"reporc to. "cr-nel,"max age":604800}"]"Server": ["cloudflare"]}} {Changes:• parsePolicy → deleted•narseMessade added: returns the raw messade strina from the response bodv (or .. )"correlation 10":"95256555-ec98-4541-b9za-adta/sboveab",•parseRetryAfter does the keyword matching itself (daily → 600, ten secondly → 10, secondly → 1)I"trace_1d":"C/aD8565-905t-4604-9405-0e5b551e5545'• Log line in executeRequest now logs message instead of policy - more informative for debuggingThe keyword-match order is the same as before (daily before ten secondly before secondly ) to avoid substringcollision.TIaTa 1 1111II MI 1 IL.nl .Ask anvthina (84L)< CodeClaude Onus 4.7 Medium271-24 UTE.Afo 4 spaces...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
18205
|
790
|
9
|
2026-05-11T11:04:03.346892+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-11/1778 /Users/lukas/.screenpipe/data/data/2026-05-11/1778497443346_m1.jpg...
|
PhpStorm
|
faVsco.js – laravel.log
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
iTerm2ShellEditViewSessionScriptsProfilesWindowHel iTerm2ShellEditViewSessionScriptsProfilesWindowHelplao|Preparation for Refi... in 56 mDEV (docker)-zshDOCKERO ₴1DEV (docker)$2APP (-zsh)H3jiminny-worker-processing-2:jiminny-worker-processing-2_00: stoppedjiminny-worker-processing-3:jiminny-worker-processing-3_00: stoppedjiminny-worker-processing-4:jiminny-worker-processing-4_00: stoppedjiminny-worker-processing-5: jiminny-worker-processing-5_00: stoppedjiminny-worker-processing-delayed: jiminny-worker-processing-delayed_00: stoppedworker-analytics:worker-analytics_00: stoppedworker-conferences:worker-conferences_00: stoppedworker-crm-update:worker-crm-update_00: stoppedworker-download:worker-download_00: stoppedworker-emails:worker-emails_00: stoppedworker-nudges:worker-nudges_00: stoppedworker:worker_00:stoppedworker-audio:worker-audio_00: stoppedworker-calendar:worker-calendar_00:stoppedworker-crm-sync:worker-crm-sync_00: stoppedartisan-schedule:artisan-schedule_00: stoppedworker-es-update:worker-es-update_00: stoppedjiminny-worker-processing-1:jiminny-worker-processing-1_00: stoppedartisan-schedule:artisan-schedule_00: startedjiminny-worker-processing-1:jiminny-worker-processing-1_00: startedjiminny-worker-processing-2:jiminny-worker-processing-2_00: startedjiminny-worker-processing-3:jiminny-worker-processing-3_00: startedjiminny-worker-processing-4:jiminny-worker-processing-4_00: startedjiminny-worker-processing-5:jiminny-worker-processing-5_00: startedjiminny-worker-processing-delayed: jiminny-worker-processing-delayed_00: startedworker:worker_00: startedworker-analytics:worker-analytics_00: startedworker-audio:worker-audio_00: startedworker-calendar:worker-calendar_00: startedworker-conferences:worker-conferences_00: startedworker-crm-sync:worker-crm-sync_00: startedworker-crm-update:worker-crm-update_00: startedworker-download:worker-download_00:startedworker-emails:worker-emails_00: startedworker-es-update:worker-es-update_00: startedworker-nudges:worker-nudges_00: startedroot@docker_lamp_1:/home/jiminny# php artisan jiminny: debugDispatching 100 MatchActivityCrmDatajobs (portal=2)Done. Watch logs and run jiminny:debug observeRateLimit to inspect cachestate.root@docker_lamp_1:/home/jiminny# php artisan jiminny: debugDispatching 100 MatchActivityCrmDatajobs (portal=2)Done.Watch logs and run jiminny:debug observeRateLimit toinspect cache state.root@docker_lamp_1:/home/jiminny# php artisan optimize:clear && supervisorctl restart all84-zshX5screenpipe"100% <8• Mon 11 May 14:04:02181O 886-zsh+DEV...
|
NULL
|
-1601892739269893101
|
NULL
|
visual_change
|
ocr
|
NULL
|
iTerm2ShellEditViewSessionScriptsProfilesWindowHel iTerm2ShellEditViewSessionScriptsProfilesWindowHelplao|Preparation for Refi... in 56 mDEV (docker)-zshDOCKERO ₴1DEV (docker)$2APP (-zsh)H3jiminny-worker-processing-2:jiminny-worker-processing-2_00: stoppedjiminny-worker-processing-3:jiminny-worker-processing-3_00: stoppedjiminny-worker-processing-4:jiminny-worker-processing-4_00: stoppedjiminny-worker-processing-5: jiminny-worker-processing-5_00: stoppedjiminny-worker-processing-delayed: jiminny-worker-processing-delayed_00: stoppedworker-analytics:worker-analytics_00: stoppedworker-conferences:worker-conferences_00: stoppedworker-crm-update:worker-crm-update_00: stoppedworker-download:worker-download_00: stoppedworker-emails:worker-emails_00: stoppedworker-nudges:worker-nudges_00: stoppedworker:worker_00:stoppedworker-audio:worker-audio_00: stoppedworker-calendar:worker-calendar_00:stoppedworker-crm-sync:worker-crm-sync_00: stoppedartisan-schedule:artisan-schedule_00: stoppedworker-es-update:worker-es-update_00: stoppedjiminny-worker-processing-1:jiminny-worker-processing-1_00: stoppedartisan-schedule:artisan-schedule_00: startedjiminny-worker-processing-1:jiminny-worker-processing-1_00: startedjiminny-worker-processing-2:jiminny-worker-processing-2_00: startedjiminny-worker-processing-3:jiminny-worker-processing-3_00: startedjiminny-worker-processing-4:jiminny-worker-processing-4_00: startedjiminny-worker-processing-5:jiminny-worker-processing-5_00: startedjiminny-worker-processing-delayed: jiminny-worker-processing-delayed_00: startedworker:worker_00: startedworker-analytics:worker-analytics_00: startedworker-audio:worker-audio_00: startedworker-calendar:worker-calendar_00: startedworker-conferences:worker-conferences_00: startedworker-crm-sync:worker-crm-sync_00: startedworker-crm-update:worker-crm-update_00: startedworker-download:worker-download_00:startedworker-emails:worker-emails_00: startedworker-es-update:worker-es-update_00: startedworker-nudges:worker-nudges_00: startedroot@docker_lamp_1:/home/jiminny# php artisan jiminny: debugDispatching 100 MatchActivityCrmDatajobs (portal=2)Done. Watch logs and run jiminny:debug observeRateLimit to inspect cachestate.root@docker_lamp_1:/home/jiminny# php artisan jiminny: debugDispatching 100 MatchActivityCrmDatajobs (portal=2)Done.Watch logs and run jiminny:debug observeRateLimit toinspect cache state.root@docker_lamp_1:/home/jiminny# php artisan optimize:clear && supervisorctl restart all84-zshX5screenpipe"100% <8• Mon 11 May 14:04:02181O 886-zsh+DEV...
|
18203
|
NULL
|
NULL
|
NULL
|
|
18206
|
791
|
8
|
2026-05-11T11:04:06.179541+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-11/1778 /Users/lukas/.screenpipe/data/data/2026-05-11/1778497446179_m2.jpg...
|
PhpStorm
|
faVsco.js – laravel.log
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
PhostormINavicarecodeFV faVsco.js°9 JY-20725-handl PhostormINavicarecodeFV faVsco.js°9 JY-20725-handle-HS-search-rate-linroledey© UserAutomatedReportsController.php© PlaybackController.php© TrackRecordingFileSizT SyncCrmEntitiesTrait.phpC) CachedCrmServiceDecorator.png© JiminnyDebugCommand.php© TrackRecordingSizeEnT ValidateSmitProspectEAjReportso RematchActivityonCrmObiectDetach.png) MatchactivityermData.pnp© Job.php© CrmActivityService.php( RateLimitExcep'© MatchCrmData.php0 Calendarn Conference© CrmObiectsResolver.phr() ProviderRateLimiter.php0 Crmclass Cuient extends BasecLient imolements HubspotcuientinterfaceM A2 A67 X3ЛY@ bullnornprivate function createBatchConfiguration(string Sobjecttype): arrayC Close352_copper• J crmobiects_ DecorareAcuivily• DummyHelpersv HubspotAccountSvncStrate> Actionsa ContactsuncStratedFields• Malournal1 Metadatalv OpportunitySyncSt>MConcerns.(c) Hubsnotl actMoC HubspotLastMo(C) Hubsnotl actMo(C) Hubsnotl actMo(C) Hubsnotl actMo(C) HubsnotSinaleS© HubspotSyncStr© HubspotWebhoo~ M Padination© HubspotPaginat© PaginationConfi(C) PaqinationState> D ProspectSearchStr:› D Redisv D ServiceTraitsTOnoortunitvsvne() SvncCrmEntitiesT SuncFieldstirait.T. WriteCrmTrait.o• M UtilsM WebhookC) BatchSvncCollectotl(c) RatchSvncRedisSe(C) ClocedDea|Stadecs@ DoalFieldsService rTacts naccod. 12 (todav 12:02'inputClass' => \HubSpot\Client\Crm\Contacts\Model\SimplePublic0bjectId::class.'api' => Sthis->getNewInstance@->crmO->contacts@->batchApiO.1t @ 1ssetSconfiqurationsSobnecttvoelo) <Inval1dArqumentExcentzon message: "Unsupported obnect tyne: Sobnectivoe-"):return SconfiaurationsiSobiecttvoel:private function prepareBatchRequest(array $batchConfig, array $crmids, array $fields): object$batchReadRequest = $batchConfig['batchReadRequest'];SinnutClacs = Chatchtonfiarinnutclasstl•Sinputs = array_map(function (ScrmId) use (SinputClass) <Sinput = new $inputClassO:Sinnut->cottd(ScomTd)•return $input;}. ScrmIds):SbatchreadRequest->setinputss1nputs).SbatchReadReguest->setProperties(Sfields):orivate function validateAoiResponse(Sresponse, strina Sobiectivoe): voidif @ Sresponse)4throw new CrmException( message: "HubSpot API returned null response for {SobiectTvnel batch read")•nnivate function nrocecsAniResults(Snesnanse)• arnav=custom.logA console (EU]= laravel.log X 4 SF jiminny@localhost]4 HS_local [jiminny@localhost]console [PKol)A console [STAGING]429x5 Ccw.*uresult.T.Y:Analyzing• Preparation tor kerl.. In oo m100% 2• Mon 11 May 14:04:05HandleHubspotRateLimitTest vCascadeHubspot Rate Limit Re+0 ..lets simplify it, just call it parseMessage and skip policy al togetherThought for 5s ›Read Client.php #L86-200b Client.php+17 -35Verity nothing broke:Command docker. tail• tests/uni/365/486 cha re tan asu sportatci Mitest, phetests/Unit/Services/Crm/Hubspot/ClientTest.php --no-coverage 2>&1 | tail -10pup92.20contzaeration: Phomes3.minnv/ohpunit.xml:D.:Time: 02:14.415. Memorv: 62.00 MB•[PASSWORD_DOTS]DD..DD..D.D..DDDDD.DDD...65 / 80 (808)OK hut there were ¿ccuec!All 80 tests still passSummaryRemoved narsePo Eicv entirelv. narseRetrvAfter now mans messade kevwords direct v to seconds without theintermediate policy conceptBefore: header • narsePolicv » nolicy constant - seconds After» header » narseMeçsade → secondsChanges:•parsePoLicy → deleted•narseMessade added: returns the raw messade strina from the response body (or .)•parseRetryAfter does the keyword matching itself (daily → 600, ten secondly → 10, secondly → 1)• Log line in executeRequest now logs message instead of policy - more informative for debuggingThe keyword-match order is the same as before (daily before ten secondly before secondly ) to avoid substringcollision..nl .Ask anvthina (84L)" CodeClaude Onus 1.7 MediumWN Windsurf Teams 2-1UTF.8io 4 spaces...
|
NULL
|
-4973702247379913419
|
NULL
|
visual_change
|
ocr
|
NULL
|
PhostormINavicarecodeFV faVsco.js°9 JY-20725-handl PhostormINavicarecodeFV faVsco.js°9 JY-20725-handle-HS-search-rate-linroledey© UserAutomatedReportsController.php© PlaybackController.php© TrackRecordingFileSizT SyncCrmEntitiesTrait.phpC) CachedCrmServiceDecorator.png© JiminnyDebugCommand.php© TrackRecordingSizeEnT ValidateSmitProspectEAjReportso RematchActivityonCrmObiectDetach.png) MatchactivityermData.pnp© Job.php© CrmActivityService.php( RateLimitExcep'© MatchCrmData.php0 Calendarn Conference© CrmObiectsResolver.phr() ProviderRateLimiter.php0 Crmclass Cuient extends BasecLient imolements HubspotcuientinterfaceM A2 A67 X3ЛY@ bullnornprivate function createBatchConfiguration(string Sobjecttype): arrayC Close352_copper• J crmobiects_ DecorareAcuivily• DummyHelpersv HubspotAccountSvncStrate> Actionsa ContactsuncStratedFields• Malournal1 Metadatalv OpportunitySyncSt>MConcerns.(c) Hubsnotl actMoC HubspotLastMo(C) Hubsnotl actMo(C) Hubsnotl actMo(C) Hubsnotl actMo(C) HubsnotSinaleS© HubspotSyncStr© HubspotWebhoo~ M Padination© HubspotPaginat© PaginationConfi(C) PaqinationState> D ProspectSearchStr:› D Redisv D ServiceTraitsTOnoortunitvsvne() SvncCrmEntitiesT SuncFieldstirait.T. WriteCrmTrait.o• M UtilsM WebhookC) BatchSvncCollectotl(c) RatchSvncRedisSe(C) ClocedDea|Stadecs@ DoalFieldsService rTacts naccod. 12 (todav 12:02'inputClass' => \HubSpot\Client\Crm\Contacts\Model\SimplePublic0bjectId::class.'api' => Sthis->getNewInstance@->crmO->contacts@->batchApiO.1t @ 1ssetSconfiqurationsSobnecttvoelo) <Inval1dArqumentExcentzon message: "Unsupported obnect tyne: Sobnectivoe-"):return SconfiaurationsiSobiecttvoel:private function prepareBatchRequest(array $batchConfig, array $crmids, array $fields): object$batchReadRequest = $batchConfig['batchReadRequest'];SinnutClacs = Chatchtonfiarinnutclasstl•Sinputs = array_map(function (ScrmId) use (SinputClass) <Sinput = new $inputClassO:Sinnut->cottd(ScomTd)•return $input;}. ScrmIds):SbatchreadRequest->setinputss1nputs).SbatchReadReguest->setProperties(Sfields):orivate function validateAoiResponse(Sresponse, strina Sobiectivoe): voidif @ Sresponse)4throw new CrmException( message: "HubSpot API returned null response for {SobiectTvnel batch read")•nnivate function nrocecsAniResults(Snesnanse)• arnav=custom.logA console (EU]= laravel.log X 4 SF jiminny@localhost]4 HS_local [jiminny@localhost]console [PKol)A console [STAGING]429x5 Ccw.*uresult.T.Y:Analyzing• Preparation tor kerl.. In oo m100% 2• Mon 11 May 14:04:05HandleHubspotRateLimitTest vCascadeHubspot Rate Limit Re+0 ..lets simplify it, just call it parseMessage and skip policy al togetherThought for 5s ›Read Client.php #L86-200b Client.php+17 -35Verity nothing broke:Command docker. tail• tests/uni/365/486 cha re tan asu sportatci Mitest, phetests/Unit/Services/Crm/Hubspot/ClientTest.php --no-coverage 2>&1 | tail -10pup92.20contzaeration: Phomes3.minnv/ohpunit.xml:D.:Time: 02:14.415. Memorv: 62.00 MB•[PASSWORD_DOTS]DD..DD..D.D..DDDDD.DDD...65 / 80 (808)OK hut there were ¿ccuec!All 80 tests still passSummaryRemoved narsePo Eicv entirelv. narseRetrvAfter now mans messade kevwords direct v to seconds without theintermediate policy conceptBefore: header • narsePolicv » nolicy constant - seconds After» header » narseMeçsade → secondsChanges:•parsePoLicy → deleted•narseMessade added: returns the raw messade strina from the response body (or .)•parseRetryAfter does the keyword matching itself (daily → 600, ten secondly → 10, secondly → 1)• Log line in executeRequest now logs message instead of policy - more informative for debuggingThe keyword-match order is the same as before (daily before ten secondly before secondly ) to avoid substringcollision..nl .Ask anvthina (84L)" CodeClaude Onus 1.7 MediumWN Windsurf Teams 2-1UTF.8io 4 spaces...
|
18204
|
NULL
|
NULL
|
NULL
|
|
18223
|
791
|
16
|
2026-05-11T11:05:15.747317+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-11/1778 /Users/lukas/.screenpipe/data/data/2026-05-11/1778497515747_m2.jpg...
|
PhpStorm
|
faVsco.js – laravel.log
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
PhostormVIewINavicarecodeWindowFV faVsco.js?9 JY-2 PhostormVIewINavicarecodeWindowFV faVsco.js?9 JY-20725-handle-HS-search-rate-limitroledey© UserAutomatedReportsController.php© PlaybackController.php(C) TranscodeParameterResclC) Hubspot/Service.onp© UserService.php© Uuid.php> D TraitsD UseCases> D User> D Utils› D Validation> OvOphp nelpers.ong© InitialFrontendState.php© Jiminny.phpc) Plan.oho© Serializer.phpC) TeamScimDetails.ohpbootstrap>C build> contia>D contrib→ database>docsM front-end> D lang> node_modules library rootM ohostan> M nublic>O resourcesv Mroutesphp api.phppnp api_vz.ongpnp console.onopnp customer_api.onppnp embedded.ongphp nealtn.onppnp scim.onophp uprotectedweb.phpphp web.phpphp webhook.php>O scriptsv O storage•aoo> M debuabar.… M frameworkv Mloas.aitianoreê audio. wav= custom.loalT SyncCrmEntitiesTrait.php© CachedCrmServiceDecorator.php© JiminnyDebugCommand.phpRematchActivityOnCrmObjectDetach.php) MatchactivityermData.pnp© Job.php© CrmActivityService.phpRateLimitException.phc© MatchCrmData.phpC) CrmobiectsResolver.pho() ProviderRateLimiter.phpclass Cuient extends BasecLient imolements HubspotcuientinterfaceM A2 A67 X3ЛYprivate function createBatchConfiguration(string Sobjecttype): array352= hubspot-journal-poll.log= laravel log< nhnunit ymus tht is= oauth-nrivate kevTacts naccod. 12 (todav 12:02'inputClass' => \HubSpot\Client\Crm\Contacts\Model\SimplePublic0bjectId::class.'api' => Sthis->getNewInstance@->crmO->contacts@->batchApiO.1t @ 1ssetSconfiqurationsSobnecttvoelo) <Inval1dArqumentExcentzon message: "Unsupported obnect tyne: Sobnectivoe-"):return SconfiaurationsiSobiecttvoel:private function prepareBatchRequest(array $batchConfig, array $crmids, array $fields): object$batchReadRequest = $batchConfig['batchReadRequest'];SinputClass = $batchConfig['inputClass'];Sinputs = array_map(function (ScrmId) use (SinputClass) <Sinput = new $inputClassO:Sinnut->cottd(ScomTd)•return $input;}. ScrmIds):SbatchreadRequest->setinputss1nputs)sSbatchReadReguest->setProperties(Sfields):orivate function validateAoiResponse(Sresponse, strina Sobiectivoe): voidif @ Sresponse)4throw new CrmException( message: "HubSpot API returned null response for {SobiectTvnel batch read")•nnivate function nrocecsAniResults(Snesnanse)• arnav=custom.logA console (EU]= laravel.log X 4 SF jiminny@localhost]4 HS_local [jiminny@localhost]console [PKol)A console [STAGING]429x5 Ccw.*uresultsT.Y:Preparation tor kerl.. In oom100% 2• Mon 11 May 14:05:15HandleHubspotRateLimitTest vCascadeHubspot Rate Limit Re+0 ..lets simplify it, just call it parseMessage and skip policy al togetherThought for 5s ›Read Client.php #L86-200b Client.php+17 -35Verity nothing broke:Command docker. tail• tests/uni/365/486 cha re tan asu sportatci Mitest, phetests/Unit/Services/Crm/Hubspot/ClientTest.php --no-coverage 2>&1 | tail -10Contideration: Phome.3.minnw/ohounit.xml•[PASSWORD_DOTS]DD..DD..D.D..DDDDD.DDD...D......65 / 80 (808)Time: 02:14.415. Memorv: 62.00 MBOK hut there were ¿csuec!All 80 tests still passSummaryRemoved narsePo Eicv entirelv. narseRetrvAfter now mans messade kevwords direct v to seconds without theintermediate policy conceptBefore: header • narsePolicv » nolicy constant - seconds After» header » narseMeçsade → secondsChanges:• parsePolicy → deleted• narseMessade added: returns the raw messade strina from the response bodv (or .• )•parseRetryAfter does the keyword matching itself (daily → 600, ten secondly → 10, secondly → 1)• Log line in executeRequest now logs message instead of policy - more informative for debuggingThe keyword-match order is the same as before (daily before ten secondly before secondly ) to avoid substringcollision..nl .Ask anvthina (84L)" CodeClaude Onus 1.7 MediumWN Windsurf Toams 2-1UTF.8io 4 spaces...
|
NULL
|
787226898319290323
|
NULL
|
click
|
ocr
|
NULL
|
PhostormVIewINavicarecodeWindowFV faVsco.js?9 JY-2 PhostormVIewINavicarecodeWindowFV faVsco.js?9 JY-20725-handle-HS-search-rate-limitroledey© UserAutomatedReportsController.php© PlaybackController.php(C) TranscodeParameterResclC) Hubspot/Service.onp© UserService.php© Uuid.php> D TraitsD UseCases> D User> D Utils› D Validation> OvOphp nelpers.ong© InitialFrontendState.php© Jiminny.phpc) Plan.oho© Serializer.phpC) TeamScimDetails.ohpbootstrap>C build> contia>D contrib→ database>docsM front-end> D lang> node_modules library rootM ohostan> M nublic>O resourcesv Mroutesphp api.phppnp api_vz.ongpnp console.onopnp customer_api.onppnp embedded.ongphp nealtn.onppnp scim.onophp uprotectedweb.phpphp web.phpphp webhook.php>O scriptsv O storage•aoo> M debuabar.… M frameworkv Mloas.aitianoreê audio. wav= custom.loalT SyncCrmEntitiesTrait.php© CachedCrmServiceDecorator.php© JiminnyDebugCommand.phpRematchActivityOnCrmObjectDetach.php) MatchactivityermData.pnp© Job.php© CrmActivityService.phpRateLimitException.phc© MatchCrmData.phpC) CrmobiectsResolver.pho() ProviderRateLimiter.phpclass Cuient extends BasecLient imolements HubspotcuientinterfaceM A2 A67 X3ЛYprivate function createBatchConfiguration(string Sobjecttype): array352= hubspot-journal-poll.log= laravel log< nhnunit ymus tht is= oauth-nrivate kevTacts naccod. 12 (todav 12:02'inputClass' => \HubSpot\Client\Crm\Contacts\Model\SimplePublic0bjectId::class.'api' => Sthis->getNewInstance@->crmO->contacts@->batchApiO.1t @ 1ssetSconfiqurationsSobnecttvoelo) <Inval1dArqumentExcentzon message: "Unsupported obnect tyne: Sobnectivoe-"):return SconfiaurationsiSobiecttvoel:private function prepareBatchRequest(array $batchConfig, array $crmids, array $fields): object$batchReadRequest = $batchConfig['batchReadRequest'];SinputClass = $batchConfig['inputClass'];Sinputs = array_map(function (ScrmId) use (SinputClass) <Sinput = new $inputClassO:Sinnut->cottd(ScomTd)•return $input;}. ScrmIds):SbatchreadRequest->setinputss1nputs)sSbatchReadReguest->setProperties(Sfields):orivate function validateAoiResponse(Sresponse, strina Sobiectivoe): voidif @ Sresponse)4throw new CrmException( message: "HubSpot API returned null response for {SobiectTvnel batch read")•nnivate function nrocecsAniResults(Snesnanse)• arnav=custom.logA console (EU]= laravel.log X 4 SF jiminny@localhost]4 HS_local [jiminny@localhost]console [PKol)A console [STAGING]429x5 Ccw.*uresultsT.Y:Preparation tor kerl.. In oom100% 2• Mon 11 May 14:05:15HandleHubspotRateLimitTest vCascadeHubspot Rate Limit Re+0 ..lets simplify it, just call it parseMessage and skip policy al togetherThought for 5s ›Read Client.php #L86-200b Client.php+17 -35Verity nothing broke:Command docker. tail• tests/uni/365/486 cha re tan asu sportatci Mitest, phetests/Unit/Services/Crm/Hubspot/ClientTest.php --no-coverage 2>&1 | tail -10Contideration: Phome.3.minnw/ohounit.xml•[PASSWORD_DOTS]DD..DD..D.D..DDDDD.DDD...D......65 / 80 (808)Time: 02:14.415. Memorv: 62.00 MBOK hut there were ¿csuec!All 80 tests still passSummaryRemoved narsePo Eicv entirelv. narseRetrvAfter now mans messade kevwords direct v to seconds without theintermediate policy conceptBefore: header • narsePolicv » nolicy constant - seconds After» header » narseMeçsade → secondsChanges:• parsePolicy → deleted• narseMessade added: returns the raw messade strina from the response bodv (or .• )•parseRetryAfter does the keyword matching itself (daily → 600, ten secondly → 10, secondly → 1)• Log line in executeRequest now logs message instead of policy - more informative for debuggingThe keyword-match order is the same as before (daily before ten secondly before secondly ) to avoid substringcollision..nl .Ask anvthina (84L)" CodeClaude Onus 1.7 MediumWN Windsurf Toams 2-1UTF.8io 4 spaces...
|
18221
|
NULL
|
NULL
|
NULL
|
|
18224
|
790
|
19
|
2026-05-11T11:05:19.281687+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-11/1778 /Users/lukas/.screenpipe/data/data/2026-05-11/1778497519281_m1.jpg...
|
PhpStorm
|
faVsco.js – laravel.log
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
JY-20725-handle-HS-search Project: faVsco.js, menu
JY-20725-handle-HS-search-rate-limit, menu
Start Listening for PHP Debug Connections
HandleHubspotRateLimitTest...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JY-20725-handle-HS-search-rate-limit, menu","depth":5,"on_screen":true,"help_text":"Git Branch: JY-20725-handle-HS-search-rate-limit","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"HandleHubspotRateLimitTest","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
1684527879063584500
|
-8348266015821985344
|
click
|
hybrid
|
NULL
|
Project: faVsco.js, menu
JY-20725-handle-HS-search Project: faVsco.js, menu
JY-20725-handle-HS-search-rate-limit, menu
Start Listening for PHP Debug Connections
HandleHubspotRateLimitTest
SlackFileEditViewGoHistoryWindowHelp• 0DOCKERcompiledeventsroutesviewsO ₴1DEV (docker)$2APP (-zsh)883Jiminny-worker-processing-delayed: jiminny-worker-processing-delayed_00:stoppedjiminny-worker-processing-2:jiminny-worker-processing-2_00: stoppedjiminny-worker-processing-3:jiminny-worker-processing-3_00: stoppedjiminny-worker-processing-4:jiminny-worker-processing-4_00:stoppedjiminny-worker-processing-5: jiminny-worker-processing-5_00:stoppedworker-analytics:worker-analytics_00: stoppedworker-crm-update:worker-crm-update_00: stoppedworker-download:worker-download_00:stoppedworker-nudges:worker-nudges_00:stoppedjiminny-worker-processing-1:jiminny-worker-processing-1_00: stoppedworker:worker_00: stoppedworker-calendar:worker-calendar_00:worker-conferences:worker-conferences_00:worker-crm-sync:worker-crm-sync_00: stoppedworker-es-update:worker-es-update_00:worker-emails:worker-emails_00: stoppedworker-audio:worker-audio_00: stoppedartisan-schedule:artisan-schedule_00: stoppedartisan-schedule:artisan-schedule_00: startedjiminny-worker-processing-1:jiminny-worker-processing-1_00: startedjiminny-worker-processing-2:jiminny-worker-processing-2_00: startedjiminny-worker-processing-3:jiminny-worker-processing-3_00: startedjiminny-worker-processing-4:jiminny-worker-processing-4_00: startedjiminny-worker-processing-5:jiminny-worker-processing-5_00:startedjiminny-worker-processing-delayed: jiminny-worker-processing-delayed_00: startedworker:worker_00: startedworker-analytics:worker-analytics_00: startedworker-audio:worker-audio_00: startedworker-calendar:worker-calendar_00:startedworker-conferences:worker-conferences_00: startedworker-crm-sync:worker-crm-sync_00: startedworker-crm-update:worker-crm-update_00: startedworker-download:worker-download_00: startedworker-emails:worker-emails_00: startedworker-es-update:worker-es-update_00:startedworker-nudges:worker-nudges_00: startedroot@docker_lamp_1:/home/jiminny#php artisan jiminny:debugDispatching 100 MatchActivityCrmDatajobs (portal=2)Done.Watch logs and runjiminny:debug observeRateLimit to inspect cache state.root@docker_lamp_1:/home/jiminny# ]DEV (dl-zshHomeDMsActivityFilesLater...Morealo)• Preparation for Refi... in 55 m100% <78• Mon 11 May 14:05:19→Describe what you are looking forJiminny ...# contusion-clinic# curiosity_lab# engineering# general# jiminny-bg# platform-tickets# product_launches# random# releases# sofia-office# support# thank-yous# the_people_of jimi...^ Direct messagesP. Aneliya Angelova®. Galya Dimitrova& Petko Kashinski&. Stefka StoyanovaVasil Vasilev&. Nikolay IvanovAneliya Angelova, ...Stoyan Tanev• VesE Lukas Kovalik y…..::: AppsS Jira CloudToastGanala Cala# thank-yous406 0• Messagest* Add canvas+O FilesWednesday, May 6th~It shows increaiuie skir tu jump into Supportand not miss a beat.More importantly, itshows great heart and helped delight ourcustomers on two ways at the same time:Support and Trials.You embodied so many of our values duringthis period.#selflessness #lowego #customerfocus#positiveattitudeView Recognition Moment• 1010.501 #1011 reply 5 days agoToday ~Greg 1:31 PMShout out to engineering/infrastructure teamand their stability work. Did you know ... last weekour Product had 100% uptime? ..not99.9999999% but /2 %!lots of little things (and a few big things) make thathappen. well done all!#1NewKara Jones 1:54 PM100!!!!!! That is amazing!!! Power team!!Message #thank-yous...
|
18222
|
NULL
|
NULL
|
NULL
|
|
18225
|
791
|
17
|
2026-05-11T11:05:22.482368+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-11/1778 /Users/lukas/.screenpipe/data/data/2026-05-11/1778497522482_m2.jpg...
|
PhpStorm
|
faVsco.js – custom.log
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
JY-20725-handle-HS-search Project: faVsco.js, menu
JY-20725-handle-HS-search-rate-limit, menu...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"bounds":{"left":0.025930852,"top":0.019952115,"width":0.03856383,"height":0.025538707},"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JY-20725-handle-HS-search-rate-limit, menu","depth":5,"bounds":{"left":0.064494684,"top":0.019952115,"width":0.09541223,"height":0.025538707},"on_screen":true,"help_text":"Git Branch: JY-20725-handle-HS-search-rate-limit","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
-5641617897080429754
|
-8160223333407913180
|
click
|
hybrid
|
NULL
|
Project: faVsco.js, menu
JY-20725-handle-HS-search Project: faVsco.js, menu
JY-20725-handle-HS-search-rate-limit, menu
PhostormVIewINavicarecodeLaravelWindowFV faVsco.js°9 JY-20725-handle-HS-search-rate-limiProiectC) TrackAutomated ReportGeneratedzventonp(C) TranscodeParameterResclT SyncCrmEntitiesTrait.php© UserService.php© Uuid.php> D TraitsD UseCases> D User> D Utils› D Validation> OvOphp nelpers.ong© InitialFrontendState.php© Jiminny.phpc) Plan.oho© Serializer.phpC) TeamScimDetails.ohpbootstrap>© build.> contia> O contrib.→ database>docsfront-end> D lang> node_modules library rootM ohostan> M nublic>O resourcesv Mroutesphp api.phppnp api_vz.ongpnp console.onopnp customer_api.onppnp embedded.ongphp nealtn.onppnp scim.onophp uprotectedweb.phpphp web.phpphp webhook.php>O scriptsv O storage•aoo>M debugbar… M frameworkv Mloas.aitianoreê audio. wav= custom.loalC) CachedCrmServiceDecorator.ongDeleteCrmEntityTrait.php© CheckAndRetryRemoteMatch.php© HandleHubspotRateLimit.phpC) Kernel.png© PaginationState.phpC)MatchermData.phgclass Client extends Baseclient 1mpLements Hubspotcllentintertaceprivate function createBatchConfiguration(string $objectType): array352'inputClass' => \HubSpot\Client\Crm\Contacts\Model\SimplePublic0bjectId::class'api' => $this->getNewInstance->crm->contacts->batchApiO.if (1 isset($configurations[$obiectTypel)) &chrow new\InvalidArgumentException( message: "Unsupported obiect type: {SobiectType}")return SconfiqurationsSobnectivoel:orivate function prenareßatchReduestarrav Shatchionfio, arnav scrmids. arrav Sfields: obnect$batchReadRequest = $batchConfigl'batchReadRequest'];SinnutClass = ShatchConfiar'innutclass'1:Sinnuts = arrav manfunction (Scrmid) use Sinnutclass) &Sinput = new $inputClassO:Ginnut->cotTd(Scomtd)•return $input;}, ScrmIds):$batchReadRequest->setInputs($inputs)ChatchPondPoauoct.sco+Dnonontioc/Cfiolde)private function validateApiResponse(Sresponse, string SobiectType): voidif (! Sresponse) {throw new CrmException( message: "HubSpot APT returned null response for {SobiectTvne} batch read"):orivate function processAniResults(Sresponse): arrav= hubspot-journal-poll.log= laravel log< nhnunit ymSresults = 1];SnpsnonseResults = Snesnanse->aetResultc0•us tht is= oauth-nrivate kevTacts naccod. 12 (todav 12:02Preparation tor kerl… In oom100% L2• Mon 11 May 14:05:22HandleHubspotRateLimitTest v« console (PROD]* console (EUl& console (STAGINGIw.19A© PlaybackController.php(C) Hubsnot/Service.ohn(c) HubSpot/Service.onpoveryaeuvitycrmlaskJoo.pnp© Job.phpC) CrmActivityService.pnp© PaginationConfig.phg=laravel.log4 SF [jiminny@localhost]& HS_local [jiminny@localhost][2026-05-07 14:21:15] local.INF0: [Hubspot] DEBUG Getting headers {"Date":["Thu,07 May 2026 14:21:15 GMT"],"Content-Type":["application/json;charset=utf-8"]."Tnancfen-Encoding": ["chunked"J,"conneccion". Keep-alive"n"CF-Ray" : ["9f80deb8db60dc3a-SOF"],"CF-Cache-Status":"DYNAMIC"]"Strict-Transport-Secur1ty":"max-aqe=31536000* 1ncludeSubDomains: preload")accept-encoding"],"access-control-allow-credentials": "false")server-timing": ["hcid;desc=\"019e02d0-6fd8-7812-bdba-885b7ccb3ee3\",cfn:desc="9-80de8ercodcSa-TAD"'"x-content-type-options": ["nosniff"]."x-hubsoot-correlation-id":"019e02d0-6fd8-7812-bdba-885b7ccb3ee3"])07-May-26 14:51:15 GMT; domain=.hubapi.com; Http0nly; Secure; SameSite=None"],"Renont-To"l."."endnoints"•!\"url\":\"https:|V/\V/a.nel.cloudflare.com/\/report\\/v4?s=NYAlsVTP0fYm32qrSDjxYE4sd2RWRqiSp3wHsmdEgZLzoYdxI%2BIxVpHmsKn30%2BKVA3mFIJ2m7YRECDGS\"group\":\"cf-nel\",\"max_age\":604800}"],"success_fraction\":0.01,l"report_to\":\"cf-nel\".\"max_age\":604800}"],"Server": ["cloudflare"]}} {"correlation_id":"95236535-ec98-4541-b92a-adfa73b69eab""c7ab8365-903f-46d4-9403-0e5b551e3545"}W Windsurf Teams 25:27 UTF-8 fo 4 spaces ®...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
18226
|
790
|
20
|
2026-05-11T11:05:25.019983+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-11/1778 /Users/lukas/.screenpipe/data/data/2026-05-11/1778497525019_m1.jpg...
|
PhpStorm
|
faVsco.js – custom.log
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
SlackFileEditViewGoHistoryWindowHelp• 0DOCKERcompi SlackFileEditViewGoHistoryWindowHelp• 0DOCKERcompiledeventsroutesviewsO ₴1DEV (docker)$2APP (-zsh)883Jiminny-worker-processing-delayed: jiminny-worker-processing-delayed_00:stoppedjiminny-worker-processing-2:jiminny-worker-processing-2_00: stoppedjiminny-worker-processing-3:jiminny-worker-processing-3_00: stoppedjiminny-worker-processing-4:jiminny-worker-processing-4_00:stoppedjiminny-worker-processing-5: jiminny-worker-processing-5_00:stoppedworker-analytics:worker-analytics_00: stoppedworker-crm-update:worker-crm-update_00: stoppedworker-download:worker-download_00:stoppedworker-nudges:worker-nudges_00:stoppedjiminny-worker-processing-1:jiminny-worker-processing-1_00: stoppedworker:worker_00: stoppedworker-calendar:worker-calendar_00:worker-conferences:worker-conferences_00:worker-crm-sync:worker-crm-sync_00: stoppedworker-es-update:worker-es-update_00:worker-emails:worker-emails_00: stoppedworker-audio:worker-audio_00: stoppedartisan-schedule:artisan-schedule_00: stoppedartisan-schedule:artisan-schedule_00: startedjiminny-worker-processing-1:jiminny-worker-processing-1_00: startedjiminny-worker-processing-2:jiminny-worker-processing-2_00: startedjiminny-worker-processing-3:jiminny-worker-processing-3_00: startedjiminny-worker-processing-4:jiminny-worker-processing-4_00: startedjiminny-worker-processing-5:jiminny-worker-processing-5_00:startedjiminny-worker-processing-delayed: jiminny-worker-processing-delayed_00: startedworker:worker_00: startedworker-analytics:worker-analytics_00: startedworker-audio:worker-audio_00: startedworker-calendar:worker-calendar_00:startedworker-conferences:worker-conferences_00: startedworker-crm-sync:worker-crm-sync_00: startedworker-crm-update:worker-crm-update_00: startedworker-download:worker-download_00: startedworker-emails:worker-emails_00: startedworker-es-update:worker-es-update_00:startedworker-nudges:worker-nudges_00: startedroot@docker_lamp_1:/home/jiminny#php artisan jiminny:debugDispatching 100 MatchActivityCrmDatajobs (portal=2)Done.Watch logs and runjiminny:debug observeRateLimit to inspect cache state.root@docker_lamp_1:/home/jiminny# ]DEV (dl-zshHomeDMsActivityFilesLater...Morealo)• Preparation for Refi... in 55 m100% <78• Mon 11 May 14:05:24→Describe what you are looking forJiminny ...# contusion-clinic# curiosity_lab# engineering# general# jiminny-bg# platform-tickets# product_launches# random# releases# sofia-office# support# thank-yous# the_people_of jimi...^ Direct messagesP. Aneliya Angelova®. Galya Dimitrova& Petko Kashinski&. Stefka StoyanovaVasil Vasilev&. Nikolay IvanovAneliya Angelova, ...Stoyan Tanev• VesE Lukas Kovalik y…..::: AppsS Jira CloudToastGanala Cala# thank-yous406 0Messagest* Add canvas+O FilesWednesday, May 6th~It shows increaiuie skir tu jump into Supportand not miss a beat.More importantly, itshows great heart and helped delight ourcustomers on two ways at the same time:Support and Trials.You embodied so many of our values duringthis period.#selflessness #lowego #customerfocus#positiveattitudeView Recognition Moment011 reply 5 days agoToday ~Greg 1:31 PMShout out to engineering/infrastructure teamand their stability work. Did you know ... last weekour Product had 100% uptime? ..not99.9999999% but /2 %!lots of little things (and a few big things) make thathappen. well done all!81#1NewKara Jones 1:54 PM100!!!!!! That is amazing!!! Power team!!Message #thank-yous+...
|
NULL
|
8148209080537704809
|
NULL
|
click
|
ocr
|
NULL
|
SlackFileEditViewGoHistoryWindowHelp• 0DOCKERcompi SlackFileEditViewGoHistoryWindowHelp• 0DOCKERcompiledeventsroutesviewsO ₴1DEV (docker)$2APP (-zsh)883Jiminny-worker-processing-delayed: jiminny-worker-processing-delayed_00:stoppedjiminny-worker-processing-2:jiminny-worker-processing-2_00: stoppedjiminny-worker-processing-3:jiminny-worker-processing-3_00: stoppedjiminny-worker-processing-4:jiminny-worker-processing-4_00:stoppedjiminny-worker-processing-5: jiminny-worker-processing-5_00:stoppedworker-analytics:worker-analytics_00: stoppedworker-crm-update:worker-crm-update_00: stoppedworker-download:worker-download_00:stoppedworker-nudges:worker-nudges_00:stoppedjiminny-worker-processing-1:jiminny-worker-processing-1_00: stoppedworker:worker_00: stoppedworker-calendar:worker-calendar_00:worker-conferences:worker-conferences_00:worker-crm-sync:worker-crm-sync_00: stoppedworker-es-update:worker-es-update_00:worker-emails:worker-emails_00: stoppedworker-audio:worker-audio_00: stoppedartisan-schedule:artisan-schedule_00: stoppedartisan-schedule:artisan-schedule_00: startedjiminny-worker-processing-1:jiminny-worker-processing-1_00: startedjiminny-worker-processing-2:jiminny-worker-processing-2_00: startedjiminny-worker-processing-3:jiminny-worker-processing-3_00: startedjiminny-worker-processing-4:jiminny-worker-processing-4_00: startedjiminny-worker-processing-5:jiminny-worker-processing-5_00:startedjiminny-worker-processing-delayed: jiminny-worker-processing-delayed_00: startedworker:worker_00: startedworker-analytics:worker-analytics_00: startedworker-audio:worker-audio_00: startedworker-calendar:worker-calendar_00:startedworker-conferences:worker-conferences_00: startedworker-crm-sync:worker-crm-sync_00: startedworker-crm-update:worker-crm-update_00: startedworker-download:worker-download_00: startedworker-emails:worker-emails_00: startedworker-es-update:worker-es-update_00:startedworker-nudges:worker-nudges_00: startedroot@docker_lamp_1:/home/jiminny#php artisan jiminny:debugDispatching 100 MatchActivityCrmDatajobs (portal=2)Done.Watch logs and runjiminny:debug observeRateLimit to inspect cache state.root@docker_lamp_1:/home/jiminny# ]DEV (dl-zshHomeDMsActivityFilesLater...Morealo)• Preparation for Refi... in 55 m100% <78• Mon 11 May 14:05:24→Describe what you are looking forJiminny ...# contusion-clinic# curiosity_lab# engineering# general# jiminny-bg# platform-tickets# product_launches# random# releases# sofia-office# support# thank-yous# the_people_of jimi...^ Direct messagesP. Aneliya Angelova®. Galya Dimitrova& Petko Kashinski&. Stefka StoyanovaVasil Vasilev&. Nikolay IvanovAneliya Angelova, ...Stoyan Tanev• VesE Lukas Kovalik y…..::: AppsS Jira CloudToastGanala Cala# thank-yous406 0Messagest* Add canvas+O FilesWednesday, May 6th~It shows increaiuie skir tu jump into Supportand not miss a beat.More importantly, itshows great heart and helped delight ourcustomers on two ways at the same time:Support and Trials.You embodied so many of our values duringthis period.#selflessness #lowego #customerfocus#positiveattitudeView Recognition Moment011 reply 5 days agoToday ~Greg 1:31 PMShout out to engineering/infrastructure teamand their stability work. Did you know ... last weekour Product had 100% uptime? ..not99.9999999% but /2 %!lots of little things (and a few big things) make thathappen. well done all!81#1NewKara Jones 1:54 PM100!!!!!! That is amazing!!! Power team!!Message #thank-yous+...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
18227
|
791
|
18
|
2026-05-11T11:05:27.775929+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-11/1778 /Users/lukas/.screenpipe/data/data/2026-05-11/1778497527775_m2.jpg...
|
PhpStorm
|
faVsco.js – laravel.log
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
PhostormVIewINavicareCodeLaravelWindowmelpFV faVsc PhostormVIewINavicareCodeLaravelWindowmelpFV faVsco.js?9 JY-20725-handle-HS-search-rate-limitroledeyC) TrackAutomated ReportGeneratedzventonp(C) TranscodeParameterResclT SyncCrmEntitiesTrait.php© UserService.php© Uuid.php> D Traits> D UseCases> D User> D Utils› D Validation> OvOphp nelpers.ong© InitialFrontendState.php© Jiminny.phpc) Plan.oho© Serializer.phpC) TeamScimDetails.ohpbootstrap>© build.>W config>D contrib→ database>docsM front-end> D lang> node_modules library rootM ohostan> M nublic>O resourcesv Mroutesphp api.phppnp api_vz.ongpnp console.onopnp customer_api.onppnp embedded.ongphp nealtn.onppnp scim.onophp uprotectedweb.phpphp web.phpphp webhook.php>O scriptsv O storage>M debugbar… M frameworkC) CachedCrmServiceDecorator.ongJiminnyDeougcommana.ongch HandleHuh© RateLimitException.php) HandleHubspotRateLimit.phpC Clent.phpx (C) Kernel.php© PaginationConfia.phdclass Cllent extends Baseclient 1mpLements Hubspotclientintertace354380private function createßatchconfiguration(string sobjectlype): array'inputClass' => \HubSpot\Client\Crm\Contacts\Model\SimplePublic0bjectId::classap1 → sch1s->gecNewinscance->crmo->concacts->bacchap101t .asserscontzqurarzonssopneccvoelpthrow new InvalidArqumentException message: "Unsupported obnect type: «Sobnecttvoe-"›return SconfzqurationsSobnectivoel:1 usageprivate function prepareBatchRequest(array $batchConfig, array $crmids, array $fields): objectShatchReadReauest = ShatchConfiar'batchReadRequest':SinputClass = $batchConfig['inputClass']:Sinputs = array_map(function (Scrmid) use (SinputClass) 1Sinput = new SinputClassO:lSinnut->cottd(ScomTd)return $input;}, ScrmIds):$batchReadRequest->setInputs($inputs):SbatchReadRequest->setProperties(Sfields):return SbatchReadRequest:private function validateAniResponse(Sresponse, strina SobiectTvne): voidlif @ Sresponse)"v Mloas.throw new CrmExcentiond message: "HubSoot APy returned null response for Sobiectivoe, batch read")aitianoreê audio. wav= custom.loal= hubspot-journal-poll.log1 usage= laravel lognnivate Function nrocecsAniReculte(Srecnanse)• arnav< nhnunit ym390us tht isSnpsults = MkE oauth-nrivate kevseResults = Snesnonse->aetRecul+s0lC) HubSpot/Service.phpoveryaeuvitycrmlaskJoo.pnp© ProviderkateLimiter.pnpм A2 A67 ×3 ^Preparation for Refi... in 55 m100% Lz• Mon 11 May 14:05:27HandleHubsootRateLimitlestv= custom.logA SF [jiminny@localhost]4 HS_local [jiminny@localhost]console [pRODl& console (EUlA console [STAGING]Q- 4298 CcW.*L2026-05-11 11:05:18J Local.INFO: LMatchActivityCrmData) Starting CRM data matching l"activity":615092,"remote_search":true,"set_configuration":2,"old_state":1"ƯẢẢẢẢẢẢẢỐẢẢẢẢẢẢLỬẢẢẢẢẢẢẢẢẢẢẢẢ ẢẢẢNA[2026-05-11 11:05:19] local.INF0: [ AsyncUpdateElasticSearch ] Entity added to Redis list {"entityType":"activities","entityId":615092,"collectionKey":"activiti[2026-05-11 11:05:19] local.INFO: [MatchActivityCrmData] Participants old state {"activity":615092,"participants":[{"id":1004102,"user_id":null,"contact_id":nul[2026-05-11 11:05:19] local.INF0: [SocialAccountService] Fetching token {"socialAccountId":1499,"provider":"hubspot"} {"correlation_id":"517cbb4c-2532-4fb8-abbc[2026-05-11 11:05:19] local.INF0: [SocialAccountService] Token retrieved {"socialAccountId":1499,"provider":"hubspot"} {"correlation_id":"517cbb4c-2532-4fb8-abb[2026-05-11 11:05:19]local.INFO: [EncryptedTokenManager] Generating access token. {"mode":"legacy"} {"correlation_id":"517cbb4c-2532-4fb8-abbc-dd98985e80a2" , "t,[2026-05-11 11:05:19] local.INF0: [Crm0wnerResolver] Integration owner matched as CRM Owner {"crm_provider":"hubspot", "crm_owner":148,"team_id":2} {"correlation[2026-05-11 11:05:20]local.INF0: [Prospect match] Cache miss, calling the API {"identifier_type":"email"."identifier":"[EMAIL]"} {"correlat;[2026-05-11 11:05:21] local.INF0: [Hubspot] Failed to fetch contact {"email":"[EMAIL]" "reason":"[404] Client error:[2026-05-11 11:05:21]local.INF0: [Prospect match] API returned empty result, caching the miss with empty prospect data {"identifier type":"email" "identifier":[2026-05-11 11:05:23] local.INF0: [Hubspot] Pagination completed {"team id":2,"endpoint":"https://api.hubapi.com/crm/v3/objects/contact/search" "total requests"[2026-05-11 11:05:25]local.INF0: [Prospect match] Resolved company domain from email {"email":"[EMAIL]"."domain":"jiminny.com"} {"correlati[2026-05-11 11:05:25] local.INF0: [Prospect match] Cache miss {"identifier type":"domain" "identifier":"jiminny.com" "crm":"hubspot"} {"correlation id":"517cbb4[2026-05-11 11:05:25] local.INF0: [Prospect match] Cache miss, calling the API {"identifier type":"domain" "identifier":"jiminny.com"} {"correlation id":"517cbbTі пивин 1111I | UTW1 IIME T0 M01111...
|
NULL
|
4329372592022958002
|
NULL
|
click
|
ocr
|
NULL
|
PhostormVIewINavicareCodeLaravelWindowmelpFV faVsc PhostormVIewINavicareCodeLaravelWindowmelpFV faVsco.js?9 JY-20725-handle-HS-search-rate-limitroledeyC) TrackAutomated ReportGeneratedzventonp(C) TranscodeParameterResclT SyncCrmEntitiesTrait.php© UserService.php© Uuid.php> D Traits> D UseCases> D User> D Utils› D Validation> OvOphp nelpers.ong© InitialFrontendState.php© Jiminny.phpc) Plan.oho© Serializer.phpC) TeamScimDetails.ohpbootstrap>© build.>W config>D contrib→ database>docsM front-end> D lang> node_modules library rootM ohostan> M nublic>O resourcesv Mroutesphp api.phppnp api_vz.ongpnp console.onopnp customer_api.onppnp embedded.ongphp nealtn.onppnp scim.onophp uprotectedweb.phpphp web.phpphp webhook.php>O scriptsv O storage>M debugbar… M frameworkC) CachedCrmServiceDecorator.ongJiminnyDeougcommana.ongch HandleHuh© RateLimitException.php) HandleHubspotRateLimit.phpC Clent.phpx (C) Kernel.php© PaginationConfia.phdclass Cllent extends Baseclient 1mpLements Hubspotclientintertace354380private function createßatchconfiguration(string sobjectlype): array'inputClass' => \HubSpot\Client\Crm\Contacts\Model\SimplePublic0bjectId::classap1 → sch1s->gecNewinscance->crmo->concacts->bacchap101t .asserscontzqurarzonssopneccvoelpthrow new InvalidArqumentException message: "Unsupported obnect type: «Sobnecttvoe-"›return SconfzqurationsSobnectivoel:1 usageprivate function prepareBatchRequest(array $batchConfig, array $crmids, array $fields): objectShatchReadReauest = ShatchConfiar'batchReadRequest':SinputClass = $batchConfig['inputClass']:Sinputs = array_map(function (Scrmid) use (SinputClass) 1Sinput = new SinputClassO:lSinnut->cottd(ScomTd)return $input;}, ScrmIds):$batchReadRequest->setInputs($inputs):SbatchReadRequest->setProperties(Sfields):return SbatchReadRequest:private function validateAniResponse(Sresponse, strina SobiectTvne): voidlif @ Sresponse)"v Mloas.throw new CrmExcentiond message: "HubSoot APy returned null response for Sobiectivoe, batch read")aitianoreê audio. wav= custom.loal= hubspot-journal-poll.log1 usage= laravel lognnivate Function nrocecsAniReculte(Srecnanse)• arnav< nhnunit ym390us tht isSnpsults = MkE oauth-nrivate kevseResults = Snesnonse->aetRecul+s0lC) HubSpot/Service.phpoveryaeuvitycrmlaskJoo.pnp© ProviderkateLimiter.pnpм A2 A67 ×3 ^Preparation for Refi... in 55 m100% Lz• Mon 11 May 14:05:27HandleHubsootRateLimitlestv= custom.logA SF [jiminny@localhost]4 HS_local [jiminny@localhost]console [pRODl& console (EUlA console [STAGING]Q- 4298 CcW.*L2026-05-11 11:05:18J Local.INFO: LMatchActivityCrmData) Starting CRM data matching l"activity":615092,"remote_search":true,"set_configuration":2,"old_state":1"ƯẢẢẢẢẢẢẢỐẢẢẢẢẢẢLỬẢẢẢẢẢẢẢẢẢẢẢẢ ẢẢẢNA[2026-05-11 11:05:19] local.INF0: [ AsyncUpdateElasticSearch ] Entity added to Redis list {"entityType":"activities","entityId":615092,"collectionKey":"activiti[2026-05-11 11:05:19] local.INFO: [MatchActivityCrmData] Participants old state {"activity":615092,"participants":[{"id":1004102,"user_id":null,"contact_id":nul[2026-05-11 11:05:19] local.INF0: [SocialAccountService] Fetching token {"socialAccountId":1499,"provider":"hubspot"} {"correlation_id":"517cbb4c-2532-4fb8-abbc[2026-05-11 11:05:19] local.INF0: [SocialAccountService] Token retrieved {"socialAccountId":1499,"provider":"hubspot"} {"correlation_id":"517cbb4c-2532-4fb8-abb[2026-05-11 11:05:19]local.INFO: [EncryptedTokenManager] Generating access token. {"mode":"legacy"} {"correlation_id":"517cbb4c-2532-4fb8-abbc-dd98985e80a2" , "t,[2026-05-11 11:05:19] local.INF0: [Crm0wnerResolver] Integration owner matched as CRM Owner {"crm_provider":"hubspot", "crm_owner":148,"team_id":2} {"correlation[2026-05-11 11:05:20]local.INF0: [Prospect match] Cache miss, calling the API {"identifier_type":"email"."identifier":"[EMAIL]"} {"correlat;[2026-05-11 11:05:21] local.INF0: [Hubspot] Failed to fetch contact {"email":"[EMAIL]" "reason":"[404] Client error:[2026-05-11 11:05:21]local.INF0: [Prospect match] API returned empty result, caching the miss with empty prospect data {"identifier type":"email" "identifier":[2026-05-11 11:05:23] local.INF0: [Hubspot] Pagination completed {"team id":2,"endpoint":"https://api.hubapi.com/crm/v3/objects/contact/search" "total requests"[2026-05-11 11:05:25]local.INF0: [Prospect match] Resolved company domain from email {"email":"[EMAIL]"."domain":"jiminny.com"} {"correlati[2026-05-11 11:05:25] local.INF0: [Prospect match] Cache miss {"identifier type":"domain" "identifier":"jiminny.com" "crm":"hubspot"} {"correlation id":"517cbb4[2026-05-11 11:05:25] local.INF0: [Prospect match] Cache miss, calling the API {"identifier type":"domain" "identifier":"jiminny.com"} {"correlation id":"517cbbTі пивин 1111I | UTW1 IIME T0 M01111...
|
18225
|
NULL
|
NULL
|
NULL
|
|
18228
|
790
|
21
|
2026-05-11T11:05:31.303354+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-11/1778 /Users/lukas/.screenpipe/data/data/2026-05-11/1778497531303_m1.jpg...
|
PhpStorm
|
faVsco.js – laravel.log
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
JY-20725-handle-HS-search Project: faVsco.js, menu
JY-20725-handle-HS-search-rate-limit, menu...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JY-20725-handle-HS-search-rate-limit, menu","depth":5,"on_screen":true,"help_text":"Git Branch: JY-20725-handle-HS-search-rate-limit","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
-5641617897080429754
|
-8160223333407913180
|
click
|
hybrid
|
NULL
|
Project: faVsco.js, menu
JY-20725-handle-HS-search Project: faVsco.js, menu
JY-20725-handle-HS-search-rate-limit, menu
SlackFileEditViewGoHistoryWindowHelp• 0DOCKERcompiledeventsroutesviewsO ₴1DEV (docker)$2APP (-zsh)883Jiminny-worker-processing-delayed: jiminny-worker-processing-delayed_00:stoppedjiminny-worker-processing-2:jiminny-worker-processing-2_00: stoppedjiminny-worker-processing-3:jiminny-worker-processing-3_00: stoppedjiminny-worker-processing-4:jiminny-worker-processing-4_00:stoppedjiminny-worker-processing-5: jiminny-worker-processing-5_00:stoppedworker-analytics:worker-analytics_00: stoppedworker-crm-update:worker-crm-update_00: stoppedworker-download:worker-download_00:stoppedworker-nudges:worker-nudges_00:stoppedjiminny-worker-processing-1:jiminny-worker-processing-1_00: stoppedworker:worker_00: stoppedworker-calendar:worker-calendar_00:worker-conferences:worker-conferences_00:worker-crm-sync:worker-crm-sync_00: stoppedworker-es-update:worker-es-update_00:worker-emails:worker-emails_00: stoppedworker-audio:worker-audio_00: stoppedartisan-schedule:artisan-schedule_00: stoppedartisan-schedule:artisan-schedule_00: startedjiminny-worker-processing-1:jiminny-worker-processing-1_00: startedjiminny-worker-processing-2:jiminny-worker-processing-2_00: startedjiminny-worker-processing-3:jiminny-worker-processing-3_00: startedjiminny-worker-processing-4:jiminny-worker-processing-4_00: startedjiminny-worker-processing-5:jiminny-worker-processing-5_00:startedjiminny-worker-processing-delayed: jiminny-worker-processing-delayed_00: startedworker:worker_00: startedworker-analytics:worker-analytics_00: startedworker-audio:worker-audio_00: startedworker-calendar:worker-calendar_00:startedworker-conferences:worker-conferences_00: startedworker-crm-sync:worker-crm-sync_00: startedworker-crm-update:worker-crm-update_00: startedworker-download:worker-download_00: startedworker-emails:worker-emails_00: startedworker-es-update:worker-es-update_00:startedworker-nudges:worker-nudges_00: startedroot@docker_lamp_1:/home/jiminny#php artisan jiminny:debugDispatching 100 MatchActivityCrmDatajobs (portal=2)Done.Watch logs and runjiminny:debug observeRateLimit to inspect cache state.root@docker_lamp_1:/home/jiminny# ]DEV (dl-zshHomeDMsActivityFilesLater...Morealo)• Preparation for Refi... in 55 m100% <78• Mon 11 May 14:05:31→Describe what you are looking forJiminny ...# contusion-clinic# curiosity_lab# engineering# general# jiminny-bg# platform-tickets# product_launches# random# releases# sofia-office# support# thank-yous# the_people_of jimi...^ Direct messagesP. Aneliya Angelova®. Galya Dimitrova& Petko Kashinski&. Stefka StoyanovaVasil Vasilev&. Nikolay IvanovAneliya Angelova, ...Stoyan Tanev• VesE Lukas Kovalik y…..::: AppsS Jira CloudToastGanala Cala# thank-yous406 0Messagest* Add canvas+O FilesWednesday, May 6th~It shows increaiuie skir tu jump into Supportand not miss a beat.More importantly, itshows great heart and helped delight ourcustomers on two ways at the same time:Support and Trials.You embodied so many of our values duringthis period.#selflessness #lowego #customerfocus#positiveattitudeView Recognition Moment05 01 X1011 reply 5 days agoToday ~Greg 1:31 PMShout out to engineering/infrastructure teamand their stability work. Did you know ... last weekour Product had 100% uptime? ..not99.9999999% but /2 %!lots of little things (and a few big things) make thathappen. well done all!81#1NewKara Jones 1:54 PM100!!!!!! That is amazing!!! Power team!!Message #thank-yous+...
|
18226
|
NULL
|
NULL
|
NULL
|
|
18229
|
791
|
19
|
2026-05-11T11:05:35.208028+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-11/1778 /Users/lukas/.screenpipe/data/data/2026-05-11/1778497535208_m2.jpg...
|
PhpStorm
|
faVsco.js – laravel.log
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
JY-20725-handle-HS-search Project: faVsco.js, menu
JY-20725-handle-HS-search-rate-limit, menu
Start Listening for PHP Debug Connections
HandleHubspotRateLimitTest...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"bounds":{"left":0.025930852,"top":0.019952115,"width":0.03856383,"height":0.025538707},"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JY-20725-handle-HS-search-rate-limit, menu","depth":5,"bounds":{"left":0.064494684,"top":0.019952115,"width":0.09541223,"height":0.025538707},"on_screen":true,"help_text":"Git Branch: JY-20725-handle-HS-search-rate-limit","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"bounds":{"left":0.82413566,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"HandleHubspotRateLimitTest","depth":6,"bounds":{"left":0.8394282,"top":0.019952115,"width":0.076130316,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
1684527879063584500
|
-8348266015821985344
|
click
|
hybrid
|
NULL
|
Project: faVsco.js, menu
JY-20725-handle-HS-search Project: faVsco.js, menu
JY-20725-handle-HS-search-rate-limit, menu
Start Listening for PHP Debug Connections
HandleHubspotRateLimitTest
PhostormVIeWINavicareCodeLaravelWindowmelpFV faVsco.js?9 JY-20725-handle-HS-search-rate-limitroledeyC) TrackAutomated ReportGeneratedzventonp(C) TranscodeParameterResclT SyncCrmEntitiesTrait.php© UserService.php© Uuid.php> D Traits> D UseCases> D User> D Utils› D Validation> OvOphp nelpers.ong© InitialFrontendState.php© Jiminny.phpc) Plan.oho© Serializer.phpC) TeamScimDetails.ohpbootstrap>C build>W config>D contrib→ database>docsM front-end> D lang> node_modules library rootM ohostan> M nublic>O resourcesv Mroutesphp api.phppnp api_vz.ongpnp console.onopnp customer_api.onppnp embedded.ongphp nealtn.onppnp scim.onophp uprotectedweb.phpphp web.phpphp webhook.php>O scriptsv O storage•aoo>M debugbar… M frameworkv Mloas.C) CachedCrmServiceDecorator.ongJiminnyDeougcommana.ongch HandleHuh© RateLimitException.php) HandleHubspotRateLimit.phpC Clent.phpx (C) Kernel.php© PaginationConfia.phdclass Cllent extends Baseclient 1mpLements Hubspotclientintertace354380private function createßatchconfiguration(string sobjectlype): array'inputClass' => \HubSpot\Client\Crm\Contacts\Model\SimplePublic0bjectId::classap1 → sch1s->gecNewinscance->crmo->concacts->bacchAp10,1t .asserscontzqurarzonssopneccvoelpthrow new InvalidArqumentException message: "Unsupported obnect type: «Sobnecttvoe-"›return SconfzqurationsSobnectivoel:1 usageprivate function prepareBatchRequest(array $batchConfig, array $crmids, array $fields): objectShatchReadReauest = ShatchConfiar'batchReadRequest':SinputClass = $batchConfig['inputClass']:Sinputs = array_map(function (Scrmid) use (SinputClass) 1Sinput = new $inputClassO:Sinnut->cottd(ScomTd)return $input;}, ScrmIds):$batchReadRequest->setInputs($inputs):SbatchReadRequest->setProperties(Sfields):return SbatchReadRequestprivate function validateAniResponse(Sresponse, strina SobiectTvne): voidlif @ Sresponse)"throw new CrmExcentiond message: "HubSoot APy returned null response for Sobiectivoe, batch read")aitianoreê audio. wav= custom.loal= hubspot-journal-poll.log1 usage= laravel lognnivate Function nrocecsAniReculte(Srecnanse)• arnav< nhnunit ymus tht isSnpsults = Mk= oauth-nrivate kevseResults = Snesnonse->aetRecultsolTacts naccod. 12 (todav 12:02(C) HubSpot/Service.phpoveryaeuvitycrmlaskJoo.pnp© ProviderkateLimiter.phpм A2 A67 ×3 ^Preparation for Refi... in 55 m100% C49. Mon 11 May 14:05:34HandleHubsnotPatel imitTect= custom.logA SF [jiminny@localhost]4 HS_local [jiminny@localhost]console [pRODlA console [EUiconsole [STAGINGIQ- 4298 CcW.*L2026-05-11 11:05:18J Local.INFO: LMatchActivityCrmData) Starting CRM data matching l"activity":615092,"remote_search":true,"set_configuration":2,"old_state":1"ƯẢẢẢẢẢẢẢỐẢẢẢẢẢẢLỬẢẢẢẢẢẢẢẢẢẢẢẢ ẢẢẢNA[2026-05-11 11:05:19] local.INF0: [ AsyncUpdateElasticSearch ] Entity added to Redis list {"entityType":"activities","entityId":615092,"collectionKey":"activiti[2026-05-11 11:05:19] local.INFO: [MatchActivityCrmData] Participants old state {"activity":615092,"participants": [{"id":1004102, "user_id":null,"contact_id":nul[2026-05-11 11:05:19] local.INF0: [SocialAccountService] Fetching token {"socialAccountId":1499,"provider":"hubspot"} {"correlation_id":"517cbb4c-2532-4fb8-abbc[2026-05-11 11:05:19] local.INF0: [SocialAccountService] Token retrieved {"socialAccountId":1499,"provider":"hubspot"} {"correlation_id":"517cbb4c-2532-4fb8-abb[2026-05-11 11:05:19]local.INFO: [EncryptedTokenManager] Generating access token. {"mode":"legacy"} {"correlation_id":"517cbb4C-2532-4fb8-abbc-dd98985e80a2" , "t;[2026-05-11 11:05:19] local.INFO: [Crm0wnerResolver] Integration owner matched as CRM Owner {"crm_provider":"hubspot", "crm_owner":148,"team_id":2} {"correlation[2026-05-11 11:05:20]local.INF0: [Prospect match] Cache miss, calling the API {"identifier_type":"email","identifier":"[EMAIL]"} {"correlat:[2026-05-11 11:05:21] local.INF0: [Hubspot] Failed to fetch contact {"email":"[EMAIL]" "reason":"[404] Client error:[2026-05-11 11:05:21]local.INF0: [Prospect match] API returned empty result, caching the miss with empty prospect data {"identifier type":"email" "identifier":[2026-05-11 11:05:23] local.INF0: [Hubspot] Pagination completed {"team id":2,"endpoint":"https://api.hubapi.com/crm/v3/objects/contact/search" "total requests"[2026-05-11 11:05:25]local.INF0: [Prospect match] Resolved company domain from email {"email":"[EMAIL]"."domain":"jiminny.com"} {"correlati[2026-05-11 11:05:25] local.INF0: [Prospect match] Cache miss {"identifier type":"domain" "identifier":"jiminny.com" "crm":"hubspot"} {"correlation id":"517cbb4[2026-05-11 11:05:25] local.INF0: [Prospect match] Cache miss.calling the API {"identifier_type":"domain" "identifier":"jiminny.com"} {"correlation id":"517cbb.Ti TuNTINI | UTW1 IIWN Windsurf TeamP A enasoc...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
18230
|
791
|
20
|
2026-05-11T11:05:42.032190+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-11/1778 /Users/lukas/.screenpipe/data/data/2026-05-11/1778497542032_m2.jpg...
|
PhpStorm
|
faVsco.js – JiminnyDebugCommand.php
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
JY-20725-handle-HS-search Project: faVsco.js, menu
JY-20725-handle-HS-search-rate-limit, menu
Start Listening for PHP Debug Connections...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"bounds":{"left":0.025930852,"top":0.019952115,"width":0.03856383,"height":0.025538707},"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JY-20725-handle-HS-search-rate-limit, menu","depth":5,"bounds":{"left":0.064494684,"top":0.019952115,"width":0.09541223,"height":0.025538707},"on_screen":true,"help_text":"Git Branch: JY-20725-handle-HS-search-rate-limit","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"bounds":{"left":0.82413566,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
-817699417600123759
|
-7159315621853853246
|
click
|
hybrid
|
NULL
|
Project: faVsco.js, menu
JY-20725-handle-HS-search Project: faVsco.js, menu
JY-20725-handle-HS-search-rate-limit, menu
Start Listening for PHP Debug Connections
PhostormVIewINavicarecodeFV faVsco.js°9 JY-20725-handle-HS-search-rate-linroledeyC. Iteratel IcersCommandC) Jiminnycacheclearco© JiminnySetEncryptedT © RateLimitExo© JiminnyTokenInfoComc) MakeslackLivecoachir© ManageScimForTeam(c) MarkBranchForEnviron© MuteOrganizerChanne 36°C) HubSpot/Service.phpPreparation for Refi... in 55 m100% C49. Mon 11 May 14:05:41HandleHubsnotPatel imitTectconsole [STAGINGIC) CachedCrmServiceDecorator.ongT DeleteCrmEntityTrait.phpoveryaeuvitycrmlaskJoo.pnp© Job.php© ProviderkateLimiter.php= | A5 A133 X11 A vclass JiminnyDebuqcommand extends Commandprivate function natelamutScrmService->matchByName('Robot'):= custom.log= laravel.l0g *A SF [jiminny@localhost]4 HS_local [jiminny@localhost]console [pRODlA console [EUiQ- 4290 00.8 CcW.*12026-05-11 11:05:18J Local.INFO: LMatchActivityCrmData) Starting CRM data matching l"activity":615092,"remote_search":true,"set_configuration":2,"old_state":1"ƯẢẢẢẢẢẢẢỐẢẢẢẢẢẢLỬẢẢẢẢẢẢẢẢẢẢẢẢ ẢẢẢNA[2026-05-11 11:05:19] local.INF0: [ AsyncUpdateElasticSearch ] Entity added to Redis list {"entityType":"activities","entityId":615092,"collectionKey":"activiti[2026-05-11 11:05:19] local.INFO: [MatchActivityCrmData] Participants old state {"activity":615092,"participants": [{"id":1004102, "user_id":null,"contact_id":nul[2026-05-11 11:05:19] local.INF0: [SocialAccountService] Fetching token {"socialAccountId":1499,"provider":"hubspot"} {"correlation_id":"517cbb4c-2532-4fb8-abbc[2026-05-11 11:05:19] local.INF0: [SocialAccountService] Token retrieved {"socialAccountId":1499,"provider":"hubspot"} {"correlation_id":"517cbb4c-2532-4fb8-abb[2026-05-11 11:05:19]local.INFO: [EncryptedTokenManager] Generating access token. {"mode":"legacy"} {"correlation_id":"517cbb4C-2532-4fb8-abbc-dd98985e80a2" , "t;[2026-05-11 11:05:19] local.INFO: [Crm0wnerResolver] Integration owner matched as CRM Owner {"crm_provider":"hubspot", "crm_owner":148,"team_id":2} {"correlation[2026-05-11 11:05:20]local.INF0: [Prospect match] Cache miss, calling the API {"identifier_type":"email","identifier":"[EMAIL]"} {"correlat:[2026-05-11 11:05:21] local.INF0: [Hubspot] Failed to fetch contact {"email":"[EMAIL]" "reason":"[404] Client error:'G5T bttne•Iland hubani[2026-05-11 11:05:21]local.INF0: [Prospect match] API returned empty result, caching the miss with empty prospect data {"identifier type":"email" "identifier":[2026-05-11 11:05:23] local.INF0: [Hubspot] Pagination completed {"team id":2,"endpoint":"https://api.hubapi.com/crm/v3/objects/contact/search" "total requests"2026-05-11 11:05:251local.INF0: [Prospect match] Resolved company domain from email {"email":"[EMAIL]"."domain":"jiminny.com"} {"correlati[2026-05-11 11:05:25] local.INFO: [Prospect match] Cache miss {"identifier_type":"domain","identifier":"jiminny.com", "crm":"hubspot"} {"correlation_id":"517cbb40[2026-05-11 11:05:25] local.INF0: [Prospect match] Cache miss.calling the API {"identifier_type":"domain" "identifier":"jiminny.com"} {"correlation id":"517cbb.(C) RemoveDeleteMarkers 575C) Remove SxoiredNudae:(C) RemoveUnusedPartici 373c) RocetslacticSearch.nh 576(c) RoctoreActivitvCrmPrc 57(C) RoctoreActivitvTvneet 378(C) PunAiCallScorinaForUr 379Uopelerenwmie crelnie(C) SendNudaeSynirationl, 38© SyncActivity.php(e) Trackimoorted.onp@ WhichWorkorleWorkin, 384m Scheduling© Kernel.php> D Contracts> D DomainDTO> 0 Emails> C Enumsv D Eventsv M Activities> _ ActivitvProvidenM AiAutomation> AudidD BotsD CoachingM ConferencesM Connections• M CrmC) ActivitvCancelled.n 401(C) ActivitvCancelledA‹ 407(C) Activitvl eadConver 405C Activitvl inkodToßriC Activitvl oaaod nhn 403(e) ActivityCchodulod r 404(e) Autol ocActivity nhi 407private function simulateMatchActivityStorm(int $teamId = 2, int $count = 100): voidSceam = leam::tindsceamla*scont10 = steam->geturmuontiquratlononSactivities = Activitv::querv@l->wherel co'crm configuration id'. Sconfig->qetIdO)>U1mit (Scount))->qetosSthis->info( string: "Daspatchina «Sactivities->countO* MatchActivitvermbata noos (portal= Sconfia->getido?"):Foreach Sactivities as sactivitv) <MatchActivitvCrmData:disoatchd..ardts: Sactivity->getId, Sconfig, true);Sthis->info( string: 'Done. Watch logs and run jiminny:debug observeRateLimit to inspect cache state.'):no usagesprivate function simulateVerifyTaskStorm(int SteamId = 2, int $count = 100): voidSactivities = Activity::queryO'team_id', $teamId)->whereNotNull( columns: 'crm_provider id')>Um1cscount)>gecoSthis->info( string: "Dnspatching «Sactivitles->countO Ver1fvActzvitvcrmtaskJob 100S")*foreach Sactivities as sactivity) ^Ver1fvAct1vitycrmtaskJob::drspatchSactzvity->qetidob:Sthis->infod strina: "Done.'):WN Windsurf Teams270-28 UTF.8P A enasoc...
|
18229
|
NULL
|
NULL
|
NULL
|
|
18231
|
790
|
22
|
2026-05-11T11:05:48.537598+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-11/1778 /Users/lukas/.screenpipe/data/data/2026-05-11/1778497548537_m1.jpg...
|
PhpStorm
|
faVsco.js – laravel.log
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
SlackFileEditViewGoHistoryWindowHelp• 0DOCKERcompi SlackFileEditViewGoHistoryWindowHelp• 0DOCKERcompiledeventsroutesviews-₴81DEV (docker)$2APP (-zsh)883Jiminny-worker-processing-delayed: jiminny-worker-processing-delayed_00:stoppedjiminny-worker-processing-2:jiminny-worker-processing-2_00: stoppedjiminny-worker-processing-3:jiminny-worker-processing-3_00: stoppedjiminny-worker-processing-4:jiminny-worker-processing-4_00:stoppedjiminny-worker-processing-5: jiminny-worker-processing-5_00:stoppedworker-analytics:worker-analytics_00: stoppedworker-crm-update:worker-crm-update_00: stoppedworker-download:worker-download_00:stoppedworker-nudges:worker-nudges_00:stoppedjiminny-worker-processing-1:jiminny-worker-processing-1_00: stoppedworker:worker_00: stoppedworker-calendar:worker-calendar_00:worker-conferences:worker-conferences_00:worker-crm-sync:worker-crm-sync_00: stoppedworker-es-update:worker-es-update_00:worker-emails:worker-emails_00: stoppedworker-audio:worker-audio_00: stoppedartisan-schedule:artisan-schedule_00: stoppedartisan-schedule:artisan-schedule_00: startedjiminny-worker-processing-1:jiminny-worker-processing-1_00: startedjiminny-worker-processing-2:jiminny-worker-processing-2_00: startedjiminny-worker-processing-3:jiminny-worker-processing-3_00: startedjiminny-worker-processing-4:jiminny-worker-processing-4_00: startedjiminny-worker-processing-5:jiminny-worker-processing-5_00:startedjiminny-worker-processing-delayed: jiminny-worker-processing-delayed_00: startedworker:worker_00: startedworker-analytics:worker-analytics_00: startedworker-audio:worker-audio_00: startedworker-calendar:worker-calendar_00:startedworker-conferences:worker-conferences_00: startedworker-crm-sync:worker-crm-sync_00: startedworker-crm-update:worker-crm-update_00: startedworker-download:worker-download_00: startedworker-emails:worker-emails_00: startedworker-es-update:worker-es-update_00:startedworker-nudges:worker-nudges_00: startedroot@docker_lamp_1:/home/jiminny#php artisan jiminny:debugDispatching 100 MatchActivityCrmDatajobs (portal=2)Done.Watch logs and runjiminny:debug observeRateLimit to inspect cache state.root@docker_lamp_1:/home/jiminny# ]DEV (dl-zshHomeDMsActivityFilesLater...Morealo)• Preparation for Refi... in 55 m100% <78• Mon 11 May 14:05:48→Describe what you are looking forJiminny ...# contusion-clinic# curiosity_lab# engineering# general# jiminny-bg# platform-tickets# product_launches# random# releases# sofia-office# support# thank-yous# the_people_of jimi...^ Direct messagesP. Aneliya Angelova®. Galya Dimitrova& Petko Kashinski&. Stefka StoyanovaVasil Vasilev&. Nikolay IvanovAneliya Angelova, ...Stoyan Tanev• VesE Lukas Kovalik y…..::: AppsS Jira CloudToastGanala Cala# thank-yous406 0Messagest* Add canvas+O FilesWednesday, May 6th~It shows increaiuie skir tu jump into Supportand not miss a beat.More importantly, itshows great heart and helped delight ourcustomers on two ways at the same time:Support and Trials.You embodied so many of our values duringthis period.#selflessness #lowego #customerfocus#positiveattitudeView Recognition Moment05 W1011 reply 5 days agoToday ~Greg 1:31 PMShout out to engineering/infrastructure teamand their stability work. Did you know ... last weekour Product had 100% uptime? ..not99.9999999% but /2 %!lots of little things (and a few big things) make thathappen. well done all!#1NewKara Jones 1:54 PM100!!!!!! That is amazing!!! Power team!!Message #thank-yous+...
|
NULL
|
-2708946406043199410
|
NULL
|
click
|
ocr
|
NULL
|
SlackFileEditViewGoHistoryWindowHelp• 0DOCKERcompi SlackFileEditViewGoHistoryWindowHelp• 0DOCKERcompiledeventsroutesviews-₴81DEV (docker)$2APP (-zsh)883Jiminny-worker-processing-delayed: jiminny-worker-processing-delayed_00:stoppedjiminny-worker-processing-2:jiminny-worker-processing-2_00: stoppedjiminny-worker-processing-3:jiminny-worker-processing-3_00: stoppedjiminny-worker-processing-4:jiminny-worker-processing-4_00:stoppedjiminny-worker-processing-5: jiminny-worker-processing-5_00:stoppedworker-analytics:worker-analytics_00: stoppedworker-crm-update:worker-crm-update_00: stoppedworker-download:worker-download_00:stoppedworker-nudges:worker-nudges_00:stoppedjiminny-worker-processing-1:jiminny-worker-processing-1_00: stoppedworker:worker_00: stoppedworker-calendar:worker-calendar_00:worker-conferences:worker-conferences_00:worker-crm-sync:worker-crm-sync_00: stoppedworker-es-update:worker-es-update_00:worker-emails:worker-emails_00: stoppedworker-audio:worker-audio_00: stoppedartisan-schedule:artisan-schedule_00: stoppedartisan-schedule:artisan-schedule_00: startedjiminny-worker-processing-1:jiminny-worker-processing-1_00: startedjiminny-worker-processing-2:jiminny-worker-processing-2_00: startedjiminny-worker-processing-3:jiminny-worker-processing-3_00: startedjiminny-worker-processing-4:jiminny-worker-processing-4_00: startedjiminny-worker-processing-5:jiminny-worker-processing-5_00:startedjiminny-worker-processing-delayed: jiminny-worker-processing-delayed_00: startedworker:worker_00: startedworker-analytics:worker-analytics_00: startedworker-audio:worker-audio_00: startedworker-calendar:worker-calendar_00:startedworker-conferences:worker-conferences_00: startedworker-crm-sync:worker-crm-sync_00: startedworker-crm-update:worker-crm-update_00: startedworker-download:worker-download_00: startedworker-emails:worker-emails_00: startedworker-es-update:worker-es-update_00:startedworker-nudges:worker-nudges_00: startedroot@docker_lamp_1:/home/jiminny#php artisan jiminny:debugDispatching 100 MatchActivityCrmDatajobs (portal=2)Done.Watch logs and runjiminny:debug observeRateLimit to inspect cache state.root@docker_lamp_1:/home/jiminny# ]DEV (dl-zshHomeDMsActivityFilesLater...Morealo)• Preparation for Refi... in 55 m100% <78• Mon 11 May 14:05:48→Describe what you are looking forJiminny ...# contusion-clinic# curiosity_lab# engineering# general# jiminny-bg# platform-tickets# product_launches# random# releases# sofia-office# support# thank-yous# the_people_of jimi...^ Direct messagesP. Aneliya Angelova®. Galya Dimitrova& Petko Kashinski&. Stefka StoyanovaVasil Vasilev&. Nikolay IvanovAneliya Angelova, ...Stoyan Tanev• VesE Lukas Kovalik y…..::: AppsS Jira CloudToastGanala Cala# thank-yous406 0Messagest* Add canvas+O FilesWednesday, May 6th~It shows increaiuie skir tu jump into Supportand not miss a beat.More importantly, itshows great heart and helped delight ourcustomers on two ways at the same time:Support and Trials.You embodied so many of our values duringthis period.#selflessness #lowego #customerfocus#positiveattitudeView Recognition Moment05 W1011 reply 5 days agoToday ~Greg 1:31 PMShout out to engineering/infrastructure teamand their stability work. Did you know ... last weekour Product had 100% uptime? ..not99.9999999% but /2 %!lots of little things (and a few big things) make thathappen. well done all!#1NewKara Jones 1:54 PM100!!!!!! That is amazing!!! Power team!!Message #thank-yous+...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
18232
|
791
|
21
|
2026-05-11T11:05:48.537697+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-11/1778 /Users/lukas/.screenpipe/data/data/2026-05-11/1778497548537_m2.jpg...
|
PhpStorm
|
faVsco.js – laravel.log
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
PhostormVIewINavicareCodeFV faVsco.js°9 JY-20725-h PhostormVIewINavicareCodeFV faVsco.js°9 JY-20725-handle-HS-search-rate-limProiect vC IteratelIcersCommandC) Jiminnycacheclearco© JiminnySetEncryptedT © RateLimitExo© JiminnyTokenInfoComc) MakeslackLivecoachir© ManageScimForTeam.(c) MarkBranchForEnviron© MuteOrganizerChannec) PhoApm.php(C) PropagateCoachinare© RecalculateDealRisksC 567(C) RemoveDeleteMarkers(C) Remove SxoiredNudae© RemoveUnusedParticil 576© ResetElasticSearch.ph@ PestoreActivitvCrmPrc 373(C) RoctoreActivitvTvneftC) PunAiCallScorinaForllt(C) SoedActivities nhn@ SendNudaeFyniration! 373© SyncActivity.php(e) Trackimoorted.onp@ WhichWorkorleWorkin, 378m Scheduling© Kernel.php> D Contracts> D Domain> ODTO> 0 Emails> C Enumsv D Eventsv M Activities> _ ActivitvProvidenM AiAutomation> AudidD BotsD CoachingM ConferencesM Connections• M CrmC ActivitvCancelled.nC) ActivitvCancelledA.C) Activitvl eadConver@ Activitvl inkodTocrC Activitvl oaaod nhn(e) ActivityCchoduledr© AutoLogActivity.phTacts naccod. 12 (todav 12:02(c) HubSpot/Service.onpC) CachedCrmServiceDecorator.ongT DeleteCrmEntityTrait.phpoveryaeuvitycrmlaskJoo.pnp© ProviderkateLimiter.phpm| A5 A133 X11 ^ Vclass JiminnyDebuqcommand extends Commandprivate function natelamitfor (Si = 0 ; $i < 3; $i++) {if ($i % 25 === 0) {scnis"pinto syneine opporcunity 11rSthis->info( string: "Matching contact {Si}"):scrmservice->syncupporcun1cy 574/20564'scrmservice->macchbyname "kobot"Preparation for Refi... in 55 m100% C49. Mon 11 May 14:05:48HandleHubsnotPatel imitTect= custom.log= laravel.l0g *A SF [jiminny@localhost]4 HS_local [jiminny@localhost]console [pRODlA console [EUiconsole [STAGINGIQ- 4298 CcW.*12026-05-11 11:05:18J Local.INFO: LMatchActivityCrmData) Starting CRM data matching l"activity":615092,"remote_search":true,"set_configuration":2,"old_state":1"ƯẢẢẢẢẢẢẢỐẢẢẢẢẢẢLỬẢẢẢẢẢẢẢẢẢẢẢẢ ẢẢẢNA[2026-05-11 11:05:19] local.INF0: [ AsyncUpdateElasticSearch ] Entity added to Redis list {"entityType":"activities","entityId":615092,"collectionKey":"activiti[2026-05-11 11:05:19] local.INFO: [MatchActivityCrmData] Participants old state {"activity":615092,"participants": [{"id":1004102, "user_id":null,"contact_id":nul[2026-05-11 11:05:19] local.INF0: [SocialAccountService] Fetching token {"socialAccountId":1499,"provider":"hubspot"} {"correlation_id":"517cbb4c-2532-4fb8-abbc[2026-05-11 11:05:19] local.INF0: [SocialAccountService] Token retrieved {"socialAccountId":1499,"provider":"hubspot"} {"correlation_id":"517cbb4c-2532-4fb8-abb[2026-05-11 11:05:19]local.INFO: [EncryptedTokenManager] Generating access token. {"mode":"legacy"} {"correlation_id":"517cbb4C-2532-4fb8-abbc-dd98985e80a2" , "t;[2026-05-11 11:05:19] local.INFO: [Crm0wnerResolver] Integration owner matched as CRM Owner {"crm_provider":"hubspot", "crm_owner":148,"team_id":2} {"correlation[2026-05-11 11:05:20]local.INF0: [Prospect match] Cache miss, calling the API {"identifier_type":"email","identifier":"[EMAIL]"} {"correlat:[2026-05-11 11:05:21] local.INF0: [Hubspot] Failed to fetch contact {"email":"[EMAIL]" "reason":"[404] Client error:'G5T bttne•Iland hubani[2026-05-11 11:05:21]local.INF0: [Prospect match] API returned empty result, caching the miss with empty prospect data {"identifier type":"email" "identifier":[2026-05-11 11:05:23] local.INF0: [Hubspot] Pagination completed {"team id":2,"endpoint":"https://api.hubapi.com/crm/v3/objects/contact/search" "total requests"[2026-05-11 11:05:25]local.INF0: [Prospect match] Resolved company domain from email {"email":"[EMAIL]"."domain":"jiminny.com"} {"correlati[2026-05-11 11:05:25] local.INF0: [Prospect match] Cache miss {"identifier type":"domain" "identifier":"jiminny.com" "crm":"hubspot"} {"correlation id":"517cbb4[2026-05-11 11:05:25] local.INF0: [Prospect match] Cache miss.calling the API {"identifier_type":"domain" "identifier":"jiminny.com"} {"correlation id":"517cbb.1 usageorivate function simulateMatchActivitvStormunt Steamid = 2. int Scount = 1000: vo1dSconfio = steam->oetcrmconflourationobSactivities = Activitv:quervoerm confiauration id' Sconfio->aetidon->orderBvDescd->Limit (Scount)]|->getO:Sthis->info( string: "Dispatching {Sactivities->count} MatchActivityCrmData jobs (portal={$config->getidO})"):fonpach (Sactivities as Cactivitv) &MatchActivityCrmData::dispatch( ...arguments: Sactivity->getId, $config, true):Sthis->info( string: 'Done. Watch logs and run jiminny:debug observeRateLimit to inspect cache state.'):no usagesprivate function simulateVerifyTaskStorm(int SteamId = 2, int $count = 100): voidSactavitles = Activity::quervol>where co'team_1d', Steamid)S:'com orovider 10')1'1d)->net05Sthis->info( string: "Dispatching (Sactivities->count} VerifyActivityCrmTaskJob jobs");foneach (Sactivities as Sactivitv) $WN Windsurf Teams270-28 LITE.8P A enasoc...
|
NULL
|
-8337901132868672232
|
NULL
|
click
|
ocr
|
NULL
|
PhostormVIewINavicareCodeFV faVsco.js°9 JY-20725-h PhostormVIewINavicareCodeFV faVsco.js°9 JY-20725-handle-HS-search-rate-limProiect vC IteratelIcersCommandC) Jiminnycacheclearco© JiminnySetEncryptedT © RateLimitExo© JiminnyTokenInfoComc) MakeslackLivecoachir© ManageScimForTeam.(c) MarkBranchForEnviron© MuteOrganizerChannec) PhoApm.php(C) PropagateCoachinare© RecalculateDealRisksC 567(C) RemoveDeleteMarkers(C) Remove SxoiredNudae© RemoveUnusedParticil 576© ResetElasticSearch.ph@ PestoreActivitvCrmPrc 373(C) RoctoreActivitvTvneftC) PunAiCallScorinaForllt(C) SoedActivities nhn@ SendNudaeFyniration! 373© SyncActivity.php(e) Trackimoorted.onp@ WhichWorkorleWorkin, 378m Scheduling© Kernel.php> D Contracts> D Domain> ODTO> 0 Emails> C Enumsv D Eventsv M Activities> _ ActivitvProvidenM AiAutomation> AudidD BotsD CoachingM ConferencesM Connections• M CrmC ActivitvCancelled.nC) ActivitvCancelledA.C) Activitvl eadConver@ Activitvl inkodTocrC Activitvl oaaod nhn(e) ActivityCchoduledr© AutoLogActivity.phTacts naccod. 12 (todav 12:02(c) HubSpot/Service.onpC) CachedCrmServiceDecorator.ongT DeleteCrmEntityTrait.phpoveryaeuvitycrmlaskJoo.pnp© ProviderkateLimiter.phpm| A5 A133 X11 ^ Vclass JiminnyDebuqcommand extends Commandprivate function natelamitfor (Si = 0 ; $i < 3; $i++) {if ($i % 25 === 0) {scnis"pinto syneine opporcunity 11rSthis->info( string: "Matching contact {Si}"):scrmservice->syncupporcun1cy 574/20564'scrmservice->macchbyname "kobot"Preparation for Refi... in 55 m100% C49. Mon 11 May 14:05:48HandleHubsnotPatel imitTect= custom.log= laravel.l0g *A SF [jiminny@localhost]4 HS_local [jiminny@localhost]console [pRODlA console [EUiconsole [STAGINGIQ- 4298 CcW.*12026-05-11 11:05:18J Local.INFO: LMatchActivityCrmData) Starting CRM data matching l"activity":615092,"remote_search":true,"set_configuration":2,"old_state":1"ƯẢẢẢẢẢẢẢỐẢẢẢẢẢẢLỬẢẢẢẢẢẢẢẢẢẢẢẢ ẢẢẢNA[2026-05-11 11:05:19] local.INF0: [ AsyncUpdateElasticSearch ] Entity added to Redis list {"entityType":"activities","entityId":615092,"collectionKey":"activiti[2026-05-11 11:05:19] local.INFO: [MatchActivityCrmData] Participants old state {"activity":615092,"participants": [{"id":1004102, "user_id":null,"contact_id":nul[2026-05-11 11:05:19] local.INF0: [SocialAccountService] Fetching token {"socialAccountId":1499,"provider":"hubspot"} {"correlation_id":"517cbb4c-2532-4fb8-abbc[2026-05-11 11:05:19] local.INF0: [SocialAccountService] Token retrieved {"socialAccountId":1499,"provider":"hubspot"} {"correlation_id":"517cbb4c-2532-4fb8-abb[2026-05-11 11:05:19]local.INFO: [EncryptedTokenManager] Generating access token. {"mode":"legacy"} {"correlation_id":"517cbb4C-2532-4fb8-abbc-dd98985e80a2" , "t;[2026-05-11 11:05:19] local.INFO: [Crm0wnerResolver] Integration owner matched as CRM Owner {"crm_provider":"hubspot", "crm_owner":148,"team_id":2} {"correlation[2026-05-11 11:05:20]local.INF0: [Prospect match] Cache miss, calling the API {"identifier_type":"email","identifier":"[EMAIL]"} {"correlat:[2026-05-11 11:05:21] local.INF0: [Hubspot] Failed to fetch contact {"email":"[EMAIL]" "reason":"[404] Client error:'G5T bttne•Iland hubani[2026-05-11 11:05:21]local.INF0: [Prospect match] API returned empty result, caching the miss with empty prospect data {"identifier type":"email" "identifier":[2026-05-11 11:05:23] local.INF0: [Hubspot] Pagination completed {"team id":2,"endpoint":"https://api.hubapi.com/crm/v3/objects/contact/search" "total requests"[2026-05-11 11:05:25]local.INF0: [Prospect match] Resolved company domain from email {"email":"[EMAIL]"."domain":"jiminny.com"} {"correlati[2026-05-11 11:05:25] local.INF0: [Prospect match] Cache miss {"identifier type":"domain" "identifier":"jiminny.com" "crm":"hubspot"} {"correlation id":"517cbb4[2026-05-11 11:05:25] local.INF0: [Prospect match] Cache miss.calling the API {"identifier_type":"domain" "identifier":"jiminny.com"} {"correlation id":"517cbb.1 usageorivate function simulateMatchActivitvStormunt Steamid = 2. int Scount = 1000: vo1dSconfio = steam->oetcrmconflourationobSactivities = Activitv:quervoerm confiauration id' Sconfio->aetidon->orderBvDescd->Limit (Scount)]|->getO:Sthis->info( string: "Dispatching {Sactivities->count} MatchActivityCrmData jobs (portal={$config->getidO})"):fonpach (Sactivities as Cactivitv) &MatchActivityCrmData::dispatch( ...arguments: Sactivity->getId, $config, true):Sthis->info( string: 'Done. Watch logs and run jiminny:debug observeRateLimit to inspect cache state.'):no usagesprivate function simulateVerifyTaskStorm(int SteamId = 2, int $count = 100): voidSactavitles = Activity::quervol>where co'team_1d', Steamid)S:'com orovider 10')1'1d)->net05Sthis->info( string: "Dispatching (Sactivities->count} VerifyActivityCrmTaskJob jobs");foneach (Sactivities as Sactivitv) $WN Windsurf Teams270-28 LITE.8P A enasoc...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
18233
|
791
|
22
|
2026-05-11T11:05:53.926033+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-11/1778 /Users/lukas/.screenpipe/data/data/2026-05-11/1778497553926_m2.jpg...
|
PhpStorm
|
faVsco.js – laravel.log
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
JY-20725-handle-HS-search Project: faVsco.js, menu
JY-20725-handle-HS-search-rate-limit, menu
Start Listening for PHP Debug Connections
HandleHubspotRateLimitTest
Run 'HandleHubspotRateLimitTest'
Debug 'HandleHubspotRateLimitTest'...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"bounds":{"left":0.025930852,"top":0.019952115,"width":0.03856383,"height":0.025538707},"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JY-20725-handle-HS-search-rate-limit, menu","depth":5,"bounds":{"left":0.064494684,"top":0.019952115,"width":0.09541223,"height":0.025538707},"on_screen":true,"help_text":"Git Branch: JY-20725-handle-HS-search-rate-limit","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"bounds":{"left":0.82413566,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"HandleHubspotRateLimitTest","depth":6,"bounds":{"left":0.8394282,"top":0.019952115,"width":0.076130316,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'HandleHubspotRateLimitTest'","depth":6,"bounds":{"left":0.9155585,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'HandleHubspotRateLimitTest'","depth":6,"bounds":{"left":0.9268617,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
-5870622292910750321
|
-8348263803913827968
|
visual_change
|
hybrid
|
NULL
|
Project: faVsco.js, menu
JY-20725-handle-HS-search Project: faVsco.js, menu
JY-20725-handle-HS-search-rate-limit, menu
Start Listening for PHP Debug Connections
HandleHubspotRateLimitTest
Run 'HandleHubspotRateLimitTest'
Debug 'HandleHubspotRateLimitTest'
PhostormVIewINavicareCodeLaravelKeractorTOOISWindowFV faVsco.js?9 JY-20725-handle-HS-search-rate-limitProiect vC) TrackAutomated ReportGeneratedzventonp(C) TranscodeParameterResc© UserService.php(c) Uuid.pnp> D TraitsD UseCases> D User> D Utils› D Validation> OvOphp nelpers.ong© InitialFrontendState.php© Jiminny.phpc) Plan.oho© Serializer.phpC) TeamScimDetails.ohpbootstrap>© build.> contia>D contrib→ database>O docsM front-end>D lang> node_modules library rootM ohostan> M nublic>O resourcesv Mroutesphp api.phpphp api_v2.phppnp console.onopnp customer_api.onppnp embedded.ongphp nealtn.onppnp scim.onophp uprotectedweb.phpphp web.phpphp webhook.php>O scriptsv O storage> M debuabar.… M frameworkv Mloas.aitianore• audio wav= custom.loalT SyncCrmEntitiesTrait.phpC) CachedCrmServiceDecorator.ong© RateLimitExce© PaginationConfia.phdclass JiminnyDebuqcommand extends Commandprivate function natelamut361for (Si = 0 ; $i < 3; $i++) {if ($i % 25 === 0) {363scnis"pinto syneine opporcunity 151r364Sthis->info( string: "Matching contact {$i}"):scrmservice->syncupporcun1cy574/20504'367scrmservice->macchbyname "kobot"368379=hubsnot-iournal-noll.log= laravel log< nhnunit ymus tht isE oauth-nrivate kevTacts naccod. 12 (todav 12-02Mon 11 May 14:05:53(C) HubSpot/Service.php© ProviderkateLimiter.phpw | 45 A133 X11 ^ v= custom.logT DeleteCrmEntityTrait.php1 usageprivate function simulateMatchActivityStorm(int Steamid = 2, int Scount = 100): voidSconfio = steam->oetcrmconflourat.ion0Sactivities = Activitv:quervoerm confiauration id' Sconfio->aetidon->orderBvDesc0i->Limit(Scount)->getO:Sthis->info( string: "Dispatching {Sactivities->count} MatchActivityCrmData jobs (portal={Sconfig->getidOh)"):fonpach (Sactivities as Cactivitv) 4MatchActivityCrmData::dispatch( ...arguments: Sactivity->getId, $config, true):Sthis->info( string: 'Done. Watch logs and run jiminny:debug observeRateLimit to inspect cache state.'):no usagesprivate function simulateVerifyTaskStorm(int SteamId = 2, int $count = 100): voidSactavitles = Activity::quervol>where co'team_1d', Steamid)s: 'crm provider id')'1d)->net05Sthis->info( string: "Dispatching (Sactivities->count} VerifyActivityCrmTaskJob jobs");foreach (Sactivities as $activity) {NNTIITA SF [jiminny@localhostA console [EUiconsole [STAGING12026-05-11 11:05:18] local.INFO: LMatchActivityCrmDataJ Starting CRM data12026-05-11 11:05:18] local.INF0: L EsUpdateTarget J Update single target[2026-05-11 11:05:19] local.INF0: [ AsyncUpdateElasticSearch ] Entity2026-05-11 11:05:191 loc01TNS0• MatchActivitvCrmDatal Particinants oldi[2026-05-11 11:05:19] local.INF0: [SocialAccountService] Fetching token[2026-05-11 11:05:19] local.INF0: [SocialAccountService] Token retrieved[2026-05-11 11:05:19] local.INF0: [Encr›[2026-05-11 11:05:19] local.INF0: [Crm([2026-05-11 11:05:20] local.INF0: [Pro:[2026-05-11 11:05:21] local.INF0: [Hub:[2026-05-11 11:05:21] local.INF0: [Pro2026-05-11 11:05:25 LocaL.INFU: HUD:2026-05-11 11:05:25 Local.INFU: Pro:2026-05-11 11:05:25 Local.LNFU: IPro2026-05-11 11:05:25 Local.INFU: Pro:[2026-05-11 11:05:271 local.INF0: [Hub.12026-05-11 1:05:2 LocaL.INFU: HUD.12026-05-11 11:05:281 LocaL.INFO:12026-05-11 11:05:281 LocaL.INF0: HubsDo2026-05-11 11:05:311 LocaL.INFO:2026-05-11 11:05:311 LocaL.INF0: Crm.2026-05-11 11:05:311 LocaL.INFO:12026-05-11 11:05:311 Locol TNF0: AsvncuodateslasticSearcL2026-05-11 11:05:32) Local.INFO: LMat12026-05-11 11:05:321 loc01TNF0: MatchActivitvCrmDatal2026-05-11 11:05:3211EsUpdateTarge112026-05-11 11:05:321 1oc01TNS0: 1 Asvnclndate=lasticSearc2026-05-11 11:05:321 10c01TNE0: MatchActivitver12026-05-11 11•05•221 Tocol TNS0• SocialAccountCenvicel[2026-05-11 11:05:33] local.INF0: [SocialAcc12026-05-11 11•05•731 1ocolTNS0• [EncnvntedTokenManaaen](2026-05-11 11:05:33] local.INF0: [Crm(2026-05-11 11:05:33] local.INF0: [Pro[2026-05-11 11:05:33] local.INF0: [Pro[2026-05-11 11:05:33] local.INF0: [Prospect[2026-05-11 11:05:33] local.INF0: [CrmActivityServicel[2026-05-11 11:05:33] local.INF0: [CrmActivityServicel[2026-05-11 11:05:33] local.INF0: [ EsUpdateTarget ] Update single target {"ta[2026-05-11 11:05:33] local.INF0: [ AsyncUpdateElasticSearch ] Entity2026-05-11 11:05:351 LocaL.INF0: Matchactivitycrmbatal Successfully matched CRM data "activity":614436."remote sea[2026-05-11 11:05:34] local.INF0: [MatchActi12026-05-11 11:05:351 LocaL.INF0: ES.12026-05-11 11:05:351 LocaL.INF0: Mat12026-05-11 11:05351 L0c0L 1NF0:SOC12026-05-11 11:05 :351 Loc0L 1NF0: Soch2026-05-11 11:05:351 LocaLTNF0:2026-05-11 11:05:351 L0c0L1NF0: 1Con2A26-...
|
18232
|
NULL
|
NULL
|
NULL
|
|
18234
|
791
|
23
|
2026-05-11T11:05:57.053483+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-11/1778 /Users/lukas/.screenpipe/data/data/2026-05-11/1778497557053_m2.jpg...
|
PhpStorm
|
faVsco.js – laravel.log
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
JY-20725-handle-HS-search Project: faVsco.js, menu
JY-20725-handle-HS-search-rate-limit, menu
Start Listening for PHP Debug Connections
HandleHubspotRateLimitTest
Run 'HandleHubspotRateLimitTest'
Debug 'HandleHubspotRateLimitTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
5
133
11
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Console\Commands;
use Carbon\Carbon;
use Carbon\CarbonImmutable;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Redis;
use InvalidArgumentException;
use Jiminny\Jobs\AutomatedReports\RequestGenerateAskJiminnyReportJob;
use Jiminny\Jobs\AutomatedReports\SendReportMailJob;
use Jiminny\Jobs\Crm\Delete\VerifyActivityCrmTaskJob;
use Jiminny\Jobs\Crm\MatchActivityCrmData;
use Jiminny\Jobs\JobDispatcherInterface;
use Jiminny\Models\Activity;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\AutomatedReportResult;
use Jiminny\Models\Team;
use Jiminny\Models\User;
use Jiminny\Repositories\AutomatedReportsRepository;
use Jiminny\Services\Activity\CrmOwnerResolver;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
/**
* Class JiminnyDebugCommand
*
* @package Jiminny\Console\Commands
*/
class JiminnyDebugCommand extends Command
{
public const string FREQUENCY_DAILY = 'daily';
public const string FREQUENCY_WEEKLY = 'weekly';
public const string FREQUENCY_MONTHLY = 'monthly';
public const string FREQUENCY_QUARTERLY = 'quarterly';
public const string FREQUENCY_ONE_OFF = 'one_off';
protected $signature = 'jiminny:debug';
public function handle(
JobDispatcherInterface $jobDispatcher,
AutomatedReportsService $automatedReportsService,
AutomatedReportsRepository $automatedReportsRepository,
UserPilotClient $userPilotClient
): void {
// Choose ONE of the following to run, then comment out the others.
// 1) Dispatch a storm of MatchActivityCrmData jobs against team 2
$this->simulateMatchActivityStorm(teamId: 2, count: 100);
// 2) Dispatch a storm of VerifyActivityCrmTaskJob jobs (simulates DeleteCrmEntityTrait fan-out)
// $this->simulateVerifyTaskStorm(teamId: 2, count: 100);
// 3) Inspect Redis circuit-breaker state for the team's HubSpot portal
// $this->observeRateLimitCache(teamId: 2);
// 4) Make 3 synchronous matchByName calls (foreground, hits API directly)
// $this->rateLimit();
exit(1);
$report = AutomatedReport::find(71);
$last = AutomatedReportResult::query()
->where('report_id', $report->getId())
->whereIn('status', [AutomatedReportResult::STATUS_DEFAULT, AutomatedReportResult::STATUS_FAILED])
// ->where('reason', '!=', AutomatedReportResult::REASON_NOT_ENOUGH_ACTIVITIES)
->whereDate('created_at', CarbonImmutable::now()->toDateString())
->latest()
->first();
$this->info("Last: {$last->getId()}");
exit(1);
$user = User::find(143);
// $count = $automatedReportsRepository->countUserReports($user);
// $this->info("Count: {$count}");
// $count = $automatedReportsRepository->countAllUserReports($user);
// $this->info("All count: {$count}");
$payload = [
'report_type' => 'ask_jiminny',
'frequency' => 'weekly',
];
$userPilotClient->track($user, 'ask-jiminny-report-generated', $payload);
exit(1);
$now = Carbon::now()->subDay(1);
$this->info("Now: {$now->toDateTimeString()}");
$weekStart = Carbon::getWeekStartsAt();
$this->info("Now: {$weekStart}");
// $from = $now->copy()->previousWeekday()->startOfDay();
// $to = $now->copy()->previousWeekday()->endOfDay();
// $fromOld = $now->copy()->subWeeks(1)->startOfDay();
// $toOld = $now->copy()->subDay()->endOfDay();
// $fromNew = $now->copy()->subWeek()->startOfWeek();
// $toNew = $now->copy()->subWeek()->endOfWeek();
// $fromOld = $now->copy()->subMonths(1)->startOfDay();
// $toOld = $now->copy()->subDay()->endOfDay();
// $fromNew = $now->copy()->subMonthNoOverflow()->startOfMonth();
// $toNew = $now->copy()->subMonthNoOverflow()->endOfMonth();
$fromOld = $now->copy()->subMonths(3)->startOfDay();
$toOld = $now->copy()->subDay()->endOfDay();
$fromNew = $now->copy()->subQuarterNoOverflow()->startOfQuarter();
$toNew = $now->copy()->subQuarterNoOverflow()->endOfQuarter();
$this->info("From old: {$fromOld->toDateTimeString()}");
$this->info("To old: {$toOld->toDateTimeString()}");
$this->info("From new: {$fromNew->toDateTimeString()}");
$this->info("To new: {$toNew->toDateTimeString()}");
exit(1);
$report = AutomatedReport::find(71);
$job = new RequestGenerateAskJiminnyReportJob($report->getUuid());
$jobDispatcher->dispatch($job);
exit(1);
// $this->formatDate($jobDispatcher);
// $this->sendMail($jobDispatcher, $automatedReportsService);
// $this->crmService();
$this->getPayload($automatedReportsService);
exit(1);
}
private function crmService()
{
$activity = Activity::find(418141);
$team = Team::find(19);
$config = $team->getCrmConfiguration();
$crmResolver = app(CrmOwnerResolver::class, [
'team' => $team,
'integrationAdmin' => $team->getOwner(),
'providerSlug' => $config->getProviderName(),
]);
$crmService = $crmResolver->prepareCrmService();
$crmService->createTranscriptNotes($activity);
}
private function sendMail(JobDispatcherInterface $jobDispatcher, AutomatedReportsService $automatedReportsService)
{
$reportUuid = '';
// $report = $automatedReportsService->getReportResult($reportUuid);
$report = AutomatedReportResult::find(275);
$validRecipients = $automatedReportsService->getValidRecipientUsers(
$report->getReport(),
includeJiminny: true,
);
$recipient = $validRecipients[0];
$fileName = $automatedReportsService->getReportFileName($report);
$typeName = $report->getReport()->getCustomName()
?? $automatedReportsService->getReportTypeName($report);
$teamsName = $automatedReportsService->getReportTeamsName($report);
$periodName = $automatedReportsService->getReportPeriodName($report);
$s3Path = $automatedReportsService->getMediaPath($report);
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$fileName ' . PHP_EOL . print_r($fileName, true));
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$typeName ' . PHP_EOL . print_r($typeName, true));
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$teamsName ' . PHP_EOL . print_r($teamsName, true));
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$periodName ' . PHP_EOL . print_r($periodName, true));
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$s3Path ' . PHP_EOL . print_r($s3Path, true));
$jobDispatcher->dispatch(
new SendReportMailJob(
reportUuid: $report->getUuid(),
s3Path: $s3Path,
recipientEmail: $recipient['email'],
recipientName: $recipient['name'] ?? null,
fileName: $fileName,
typeName: $typeName,
teamsName: $teamsName,
periodName: $periodName,
isAskJiminny: true,
)
);
exit(1);
}
private function formatDate(JobDispatcherInterface $jobDispatcher): void
{
$customName = 'Custom report name';
// $frequency = self::FREQUENCY_DAILY;
// $frequency = self::FREQUENCY_WEEKLY;
$frequency = self::FREQUENCY_MONTHLY;
// $frequency = self::FREQUENCY_QUARTERLY;
// $frequency = self::FREQUENCY_ONE_OFF;
$period = $this->calculateFromAndToDatePeriod($frequency);
$from = $period['fromDate'];
$to = $period['toDate'];
$periodName = $this->formatReportPeriodName($frequency, $from, $to);
$filenameSuffix = null;
if ($customName) {
if ($filenameSuffix) {
$customName .= " {$filenameSuffix}";
}
$result = $this->sanitizeFileName("{$customName} - {$periodName}");
}
$this->info($result);
}
public function calculateFromAndToDatePeriod(
string $frequency,
?Carbon $fromDate = null,
?Carbon $toDate = null
): array {
if ($frequency === self::FREQUENCY_ONE_OFF) {
return [
'fromDate' => $fromDate,
'toDate' => $toDate,
];
}
$now = Carbon::now();
return match ($frequency) {
self::FREQUENCY_DAILY => [
'fromDate' => $now->copy()->subDay()->startOfDay(),
'toDate' => $now->copy()->subDay()->endOfDay(),
],
self::FREQUENCY_WEEKLY => [
'fromDate' => $now->copy()->subWeeks(1)->startOfDay(),
'toDate' => $now->copy()->subDay()->endOfDay(),
],
self::FREQUENCY_MONTHLY => [
'fromDate' => $now->copy()->subMonths(1)->startOfDay(),
'toDate' => $now->copy()->subDay()->endOfDay(),
],
self::FREQUENCY_QUARTERLY => [
'fromDate' => $now->copy()->subMonths(3)->startOfDay(),
'toDate' => $now->copy()->subDay()->endOfDay(),
],
default => throw new InvalidArgumentException("Unsupported frequency: {$frequency}"),
};
}
private function formatReportPeriodName(string $frequency, Carbon $from, Carbon $to): string
{
$fromYear = $from->format('Y');
$toYear = $to->format('Y');
$differentYears = $fromYear !== $toYear;
switch ($frequency) {
case self::FREQUENCY_DAILY:
return $from->format('j M Y');
case self::FREQUENCY_QUARTERLY:
// 'Jan-Mar 2025' or 'Nov 2024-Jan 2025' if years differ
$startMonth = $from->format('M');
$endMonth = $to->copy()->subMonth();
$endMonthName = $endMonth->format('M');
$endMonthYear = $endMonth->format('Y');
if ($differentYears) {
return "{$startMonth} {$fromYear} - {$endMonthName} {$endMonthYear}";
}
return "{$startMonth} - {$endMonthName} {$toYear}";
case self::FREQUENCY_MONTHLY:
// 'May 2025' - monthly reports are always within the same year
return $from->format('M Y');
case self::FREQUENCY_WEEKLY:
// '4 - 8 Aug 2025', '27 Oct - 3 Nov 2025', or '28 Dec 2024 - 3 Jan 2025' if years differ
$startDay = $from->format('j');
$endDay = $to->format('j');
$startMonth = $from->format('M');
$endMonth = $to->format('M');
if ($differentYears) {
return "{$startDay} {$startMonth} {$fromYear} - {$endDay} {$endMonth} {$toYear}";
}
if ($startMonth !== $endMonth) {
return "{$startDay} {$startMonth} - {$endDay} {$endMonth} {$toYear}";
}
return "{$startDay} - {$endDay} {$endMonth} {$toYear}";
case self::FREQUENCY_ONE_OFF:
// '2 May-31 May 2025' or '15 Dec 2024-15 Jan 2025' if years differ
$startDay = $from->format('j');
$startMonth = $from->format('M');
$endDay = $to->format('j');
$endMonth = $to->format('M');
// If same month and year, use a format like '2-31 May 2025'
if ($startMonth === $endMonth && ! $differentYears) {
return "{$startDay} - {$endDay} {$startMonth} {$toYear}";
}
// If different years, include both years
if ($differentYears) {
return "{$startDay} {$startMonth} {$fromYear} - {$endDay} {$endMonth} {$toYear}";
}
// Same year but different months
return "{$startDay} {$startMonth} - {$endDay} {$endMonth} {$toYear}";
default:
// Default format for unknown frequencies
return $from->format('j M Y') . ' - ' . $to->format('j M Y');
}
}
public function sanitizeFileName(string $fileName): string
{
return str_replace(['/', '\\'], '-', $fileName);
}
private function getPayload(AutomatedReportsService $automatedReportsService)
{
$reportResult = AutomatedReportResult::find(269);
$automatedReport = $reportResult->getReport();
$activityIds = [1,2,3];
$payload = $automatedReportsService->getAskJiminnyGenerateReportPayload(
automatedReport: $automatedReport,
reportResult: $reportResult,
activityIds: $activityIds,
);
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$payload ' . PHP_EOL . print_r($payload, true));
}
private function rateLimit()
{
$team = Team::find(2);
$config = $team->getCrmConfiguration();
$crmResolver = app(CrmOwnerResolver::class, [
'team' => $team,
'integrationAdmin' => $team->getOwner(),
'providerSlug' => $config->getProviderName(),
]);
$crmService = $crmResolver->prepareCrmService();
for ($i = 0 ; $i < 3; $i++) {
// if ($i % 25 === 0) {
// $this->info("Syncing opportunity {$i}");
$this->info("Matching contact {$i}");
// }
// $crmService->syncOpportunity('374720564');
$crmService->matchByName('Robot');
}
}
private function simulateMatchActivityStorm(int $teamId = 2, int $count = 100): void
{
$team = Team::find($teamId);
$config = $team->getCrmConfiguration();
$activities = Activity::query()
->where('crm_configuration_id', $config->getId())
->orderByDesc('id')
->limit($count)
->get();
$this->info("Dispatching {$activities->count()} MatchActivityCrmData jobs (portal={$config->getId()})");
foreach ($activities as $activity) {
MatchActivityCrmData::dispatch($activity->getId(), $config, true);
}
$this->info('Done. Watch logs and run jiminny:debug observeRateLimit to inspect cache state.');
}
private function simulateVerifyTaskStorm(int $teamId = 2, int $count = 100): void
{
$activities = Activity::query()
->where('team_id', $teamId)
->whereNotNull('crm_provider_id')
->orderByDesc('id')
->limit($count)
->get();
$this->info("Dispatching {$activities->count()} VerifyActivityCrmTaskJob jobs");
foreach ($activities as $activity) {
VerifyActivityCrmTaskJob::dispatch($activity->getId());
}
$this->info('Done.');
}
private function observeRateLimitCache(int $teamId = 2): void
{
$team = Team::find($teamId);
$config = $team->getCrmConfiguration();
$key = sprintf('hubspot:ratelimit:portal:%d', $config->getId());
$value = Redis::get($key);
$ttl = Redis::ttl($key);
$this->info("Redis key: {$key}");
$this->info('Value: ' . ($value ?? '(empty)'));
$this->info("TTL: {$ttl}s");
}
}
Show Replace Field
Search History...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"bounds":{"left":0.025930852,"top":0.019952115,"width":0.03856383,"height":0.025538707},"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JY-20725-handle-HS-search-rate-limit, menu","depth":5,"bounds":{"left":0.064494684,"top":0.019952115,"width":0.09541223,"height":0.025538707},"on_screen":true,"help_text":"Git Branch: JY-20725-handle-HS-search-rate-limit","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"bounds":{"left":0.82413566,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"HandleHubspotRateLimitTest","depth":6,"bounds":{"left":0.8394282,"top":0.019952115,"width":0.076130316,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'HandleHubspotRateLimitTest'","depth":6,"bounds":{"left":0.9155585,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'HandleHubspotRateLimitTest'","depth":6,"bounds":{"left":0.9268617,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"bounds":{"left":0.9381649,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"bounds":{"left":0.96609044,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"bounds":{"left":0.9773936,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"bounds":{"left":0.9886968,"top":0.019952115,"width":0.011303186,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"5","depth":4,"bounds":{"left":0.5043218,"top":0.17478053,"width":0.007978723,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"133","depth":4,"bounds":{"left":0.5142952,"top":0.17478053,"width":0.011968086,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"11","depth":4,"bounds":{"left":0.52825797,"top":0.17478053,"width":0.008976064,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.53889626,"top":0.17318435,"width":0.00731383,"height":0.018355945},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.5462101,"top":0.17318435,"width":0.006981383,"height":0.018355945},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Console\\Commands;\n\nuse Carbon\\Carbon;\nuse Carbon\\CarbonImmutable;\nuse Illuminate\\Console\\Command;\nuse Illuminate\\Support\\Facades\\Redis;\nuse InvalidArgumentException;\nuse Jiminny\\Jobs\\AutomatedReports\\RequestGenerateAskJiminnyReportJob;\nuse Jiminny\\Jobs\\AutomatedReports\\SendReportMailJob;\nuse Jiminny\\Jobs\\Crm\\Delete\\VerifyActivityCrmTaskJob;\nuse Jiminny\\Jobs\\Crm\\MatchActivityCrmData;\nuse Jiminny\\Jobs\\JobDispatcherInterface;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\AutomatedReportResult;\nuse Jiminny\\Models\\Team;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Repositories\\AutomatedReportsRepository;\nuse Jiminny\\Services\\Activity\\CrmOwnerResolver;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\n\n/**\n * Class JiminnyDebugCommand\n *\n * @package Jiminny\\Console\\Commands\n */\nclass JiminnyDebugCommand extends Command\n{\n public const string FREQUENCY_DAILY = 'daily';\n public const string FREQUENCY_WEEKLY = 'weekly';\n public const string FREQUENCY_MONTHLY = 'monthly';\n public const string FREQUENCY_QUARTERLY = 'quarterly';\n public const string FREQUENCY_ONE_OFF = 'one_off';\n protected $signature = 'jiminny:debug';\n\n public function handle(\n JobDispatcherInterface $jobDispatcher,\n AutomatedReportsService $automatedReportsService,\n AutomatedReportsRepository $automatedReportsRepository,\n UserPilotClient $userPilotClient\n ): void {\n // Choose ONE of the following to run, then comment out the others.\n // 1) Dispatch a storm of MatchActivityCrmData jobs against team 2\n $this->simulateMatchActivityStorm(teamId: 2, count: 100);\n\n // 2) Dispatch a storm of VerifyActivityCrmTaskJob jobs (simulates DeleteCrmEntityTrait fan-out)\n // $this->simulateVerifyTaskStorm(teamId: 2, count: 100);\n\n // 3) Inspect Redis circuit-breaker state for the team's HubSpot portal\n // $this->observeRateLimitCache(teamId: 2);\n\n // 4) Make 3 synchronous matchByName calls (foreground, hits API directly)\n // $this->rateLimit();\n exit(1);\n\n\n\n $report = AutomatedReport::find(71);\n $last = AutomatedReportResult::query()\n ->where('report_id', $report->getId())\n ->whereIn('status', [AutomatedReportResult::STATUS_DEFAULT, AutomatedReportResult::STATUS_FAILED])\n// ->where('reason', '!=', AutomatedReportResult::REASON_NOT_ENOUGH_ACTIVITIES)\n ->whereDate('created_at', CarbonImmutable::now()->toDateString())\n ->latest()\n ->first();\n\n $this->info(\"Last: {$last->getId()}\");\n\n exit(1);\n\n $user = User::find(143);\n // $count = $automatedReportsRepository->countUserReports($user);\n // $this->info(\"Count: {$count}\");\n // $count = $automatedReportsRepository->countAllUserReports($user);\n // $this->info(\"All count: {$count}\");\n\n $payload = [\n 'report_type' => 'ask_jiminny',\n 'frequency' => 'weekly',\n ];\n $userPilotClient->track($user, 'ask-jiminny-report-generated', $payload);\n\n exit(1);\n\n $now = Carbon::now()->subDay(1);\n $this->info(\"Now: {$now->toDateTimeString()}\");\n $weekStart = Carbon::getWeekStartsAt();\n $this->info(\"Now: {$weekStart}\");\n\n // $from = $now->copy()->previousWeekday()->startOfDay();\n // $to = $now->copy()->previousWeekday()->endOfDay();\n\n // $fromOld = $now->copy()->subWeeks(1)->startOfDay();\n // $toOld = $now->copy()->subDay()->endOfDay();\n // $fromNew = $now->copy()->subWeek()->startOfWeek();\n // $toNew = $now->copy()->subWeek()->endOfWeek();\n\n // $fromOld = $now->copy()->subMonths(1)->startOfDay();\n // $toOld = $now->copy()->subDay()->endOfDay();\n // $fromNew = $now->copy()->subMonthNoOverflow()->startOfMonth();\n // $toNew = $now->copy()->subMonthNoOverflow()->endOfMonth();\n\n $fromOld = $now->copy()->subMonths(3)->startOfDay();\n $toOld = $now->copy()->subDay()->endOfDay();\n $fromNew = $now->copy()->subQuarterNoOverflow()->startOfQuarter();\n $toNew = $now->copy()->subQuarterNoOverflow()->endOfQuarter();\n\n $this->info(\"From old: {$fromOld->toDateTimeString()}\");\n $this->info(\"To old: {$toOld->toDateTimeString()}\");\n $this->info(\"From new: {$fromNew->toDateTimeString()}\");\n $this->info(\"To new: {$toNew->toDateTimeString()}\");\n\n exit(1);\n\n $report = AutomatedReport::find(71);\n\n $job = new RequestGenerateAskJiminnyReportJob($report->getUuid());\n $jobDispatcher->dispatch($job);\n\n exit(1);\n\n\n // $this->formatDate($jobDispatcher);\n // $this->sendMail($jobDispatcher, $automatedReportsService);\n // $this->crmService();\n\n $this->getPayload($automatedReportsService);\n\n exit(1);\n }\n\n\n\n private function crmService()\n {\n $activity = Activity::find(418141);\n\n $team = Team::find(19);\n $config = $team->getCrmConfiguration();\n\n $crmResolver = app(CrmOwnerResolver::class, [\n 'team' => $team,\n 'integrationAdmin' => $team->getOwner(),\n 'providerSlug' => $config->getProviderName(),\n ]);\n\n $crmService = $crmResolver->prepareCrmService();\n\n $crmService->createTranscriptNotes($activity);\n }\n\n private function sendMail(JobDispatcherInterface $jobDispatcher, AutomatedReportsService $automatedReportsService)\n {\n $reportUuid = '';\n // $report = $automatedReportsService->getReportResult($reportUuid);\n $report = AutomatedReportResult::find(275);\n $validRecipients = $automatedReportsService->getValidRecipientUsers(\n $report->getReport(),\n includeJiminny: true,\n );\n\n $recipient = $validRecipients[0];\n\n $fileName = $automatedReportsService->getReportFileName($report);\n $typeName = $report->getReport()->getCustomName()\n ?? $automatedReportsService->getReportTypeName($report);\n $teamsName = $automatedReportsService->getReportTeamsName($report);\n $periodName = $automatedReportsService->getReportPeriodName($report);\n $s3Path = $automatedReportsService->getMediaPath($report);\n\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$fileName ' . PHP_EOL . print_r($fileName, true));\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$typeName ' . PHP_EOL . print_r($typeName, true));\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$teamsName ' . PHP_EOL . print_r($teamsName, true));\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$periodName ' . PHP_EOL . print_r($periodName, true));\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$s3Path ' . PHP_EOL . print_r($s3Path, true));\n\n $jobDispatcher->dispatch(\n new SendReportMailJob(\n reportUuid: $report->getUuid(),\n s3Path: $s3Path,\n recipientEmail: $recipient['email'],\n recipientName: $recipient['name'] ?? null,\n fileName: $fileName,\n typeName: $typeName,\n teamsName: $teamsName,\n periodName: $periodName,\n isAskJiminny: true,\n )\n );\n\n exit(1);\n }\n\n private function formatDate(JobDispatcherInterface $jobDispatcher): void\n {\n $customName = 'Custom report name';\n // $frequency = self::FREQUENCY_DAILY;\n // $frequency = self::FREQUENCY_WEEKLY;\n $frequency = self::FREQUENCY_MONTHLY;\n // $frequency = self::FREQUENCY_QUARTERLY;\n // $frequency = self::FREQUENCY_ONE_OFF;\n $period = $this->calculateFromAndToDatePeriod($frequency);\n $from = $period['fromDate'];\n $to = $period['toDate'];\n $periodName = $this->formatReportPeriodName($frequency, $from, $to);\n $filenameSuffix = null;\n\n if ($customName) {\n if ($filenameSuffix) {\n $customName .= \" {$filenameSuffix}\";\n }\n\n $result = $this->sanitizeFileName(\"{$customName} - {$periodName}\");\n }\n\n $this->info($result);\n }\n\n public function calculateFromAndToDatePeriod(\n string $frequency,\n ?Carbon $fromDate = null,\n ?Carbon $toDate = null\n ): array {\n if ($frequency === self::FREQUENCY_ONE_OFF) {\n return [\n 'fromDate' => $fromDate,\n 'toDate' => $toDate,\n ];\n }\n\n $now = Carbon::now();\n\n return match ($frequency) {\n self::FREQUENCY_DAILY => [\n 'fromDate' => $now->copy()->subDay()->startOfDay(),\n 'toDate' => $now->copy()->subDay()->endOfDay(),\n ],\n self::FREQUENCY_WEEKLY => [\n 'fromDate' => $now->copy()->subWeeks(1)->startOfDay(),\n 'toDate' => $now->copy()->subDay()->endOfDay(),\n ],\n self::FREQUENCY_MONTHLY => [\n 'fromDate' => $now->copy()->subMonths(1)->startOfDay(),\n 'toDate' => $now->copy()->subDay()->endOfDay(),\n ],\n self::FREQUENCY_QUARTERLY => [\n 'fromDate' => $now->copy()->subMonths(3)->startOfDay(),\n 'toDate' => $now->copy()->subDay()->endOfDay(),\n ],\n default => throw new InvalidArgumentException(\"Unsupported frequency: {$frequency}\"),\n };\n }\n\n private function formatReportPeriodName(string $frequency, Carbon $from, Carbon $to): string\n {\n $fromYear = $from->format('Y');\n $toYear = $to->format('Y');\n $differentYears = $fromYear !== $toYear;\n\n switch ($frequency) {\n case self::FREQUENCY_DAILY:\n return $from->format('j M Y');\n\n case self::FREQUENCY_QUARTERLY:\n // 'Jan-Mar 2025' or 'Nov 2024-Jan 2025' if years differ\n $startMonth = $from->format('M');\n $endMonth = $to->copy()->subMonth();\n $endMonthName = $endMonth->format('M');\n $endMonthYear = $endMonth->format('Y');\n\n if ($differentYears) {\n return \"{$startMonth} {$fromYear} - {$endMonthName} {$endMonthYear}\";\n }\n\n return \"{$startMonth} - {$endMonthName} {$toYear}\";\n\n case self::FREQUENCY_MONTHLY:\n // 'May 2025' - monthly reports are always within the same year\n return $from->format('M Y');\n\n case self::FREQUENCY_WEEKLY:\n // '4 - 8 Aug 2025', '27 Oct - 3 Nov 2025', or '28 Dec 2024 - 3 Jan 2025' if years differ\n $startDay = $from->format('j');\n $endDay = $to->format('j');\n $startMonth = $from->format('M');\n $endMonth = $to->format('M');\n\n if ($differentYears) {\n return \"{$startDay} {$startMonth} {$fromYear} - {$endDay} {$endMonth} {$toYear}\";\n }\n\n if ($startMonth !== $endMonth) {\n return \"{$startDay} {$startMonth} - {$endDay} {$endMonth} {$toYear}\";\n }\n\n return \"{$startDay} - {$endDay} {$endMonth} {$toYear}\";\n\n case self::FREQUENCY_ONE_OFF:\n // '2 May-31 May 2025' or '15 Dec 2024-15 Jan 2025' if years differ\n $startDay = $from->format('j');\n $startMonth = $from->format('M');\n $endDay = $to->format('j');\n $endMonth = $to->format('M');\n\n // If same month and year, use a format like '2-31 May 2025'\n if ($startMonth === $endMonth && ! $differentYears) {\n return \"{$startDay} - {$endDay} {$startMonth} {$toYear}\";\n }\n\n // If different years, include both years\n if ($differentYears) {\n return \"{$startDay} {$startMonth} {$fromYear} - {$endDay} {$endMonth} {$toYear}\";\n }\n\n // Same year but different months\n return \"{$startDay} {$startMonth} - {$endDay} {$endMonth} {$toYear}\";\n\n default:\n // Default format for unknown frequencies\n return $from->format('j M Y') . ' - ' . $to->format('j M Y');\n }\n }\n\n public function sanitizeFileName(string $fileName): string\n {\n return str_replace(['/', '\\\\'], '-', $fileName);\n }\n\n private function getPayload(AutomatedReportsService $automatedReportsService)\n {\n $reportResult = AutomatedReportResult::find(269);\n $automatedReport = $reportResult->getReport();\n $activityIds = [1,2,3];\n $payload = $automatedReportsService->getAskJiminnyGenerateReportPayload(\n automatedReport: $automatedReport,\n reportResult: $reportResult,\n activityIds: $activityIds,\n );\n\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$payload ' . PHP_EOL . print_r($payload, true));\n }\n\n private function rateLimit()\n {\n $team = Team::find(2);\n $config = $team->getCrmConfiguration();\n\n $crmResolver = app(CrmOwnerResolver::class, [\n 'team' => $team,\n 'integrationAdmin' => $team->getOwner(),\n 'providerSlug' => $config->getProviderName(),\n ]);\n\n $crmService = $crmResolver->prepareCrmService();\n\n for ($i = 0 ; $i < 3; $i++) {\n// if ($i % 25 === 0) {\n// $this->info(\"Syncing opportunity {$i}\");\n $this->info(\"Matching contact {$i}\");\n// }\n// $crmService->syncOpportunity('374720564');\n $crmService->matchByName('Robot');\n }\n }\n\n private function simulateMatchActivityStorm(int $teamId = 2, int $count = 100): void\n {\n $team = Team::find($teamId);\n $config = $team->getCrmConfiguration();\n\n $activities = Activity::query()\n ->where('crm_configuration_id', $config->getId())\n ->orderByDesc('id')\n ->limit($count)\n ->get();\n\n $this->info(\"Dispatching {$activities->count()} MatchActivityCrmData jobs (portal={$config->getId()})\");\n\n foreach ($activities as $activity) {\n MatchActivityCrmData::dispatch($activity->getId(), $config, true);\n }\n\n $this->info('Done. Watch logs and run jiminny:debug observeRateLimit to inspect cache state.');\n }\n\n private function simulateVerifyTaskStorm(int $teamId = 2, int $count = 100): void\n {\n $activities = Activity::query()\n ->where('team_id', $teamId)\n ->whereNotNull('crm_provider_id')\n ->orderByDesc('id')\n ->limit($count)\n ->get();\n\n $this->info(\"Dispatching {$activities->count()} VerifyActivityCrmTaskJob jobs\");\n\n foreach ($activities as $activity) {\n VerifyActivityCrmTaskJob::dispatch($activity->getId());\n }\n\n $this->info('Done.');\n }\n\n private function observeRateLimitCache(int $teamId = 2): void\n {\n $team = Team::find($teamId);\n $config = $team->getCrmConfiguration();\n $key = sprintf('hubspot:ratelimit:portal:%d', $config->getId());\n\n $value = Redis::get($key);\n $ttl = Redis::ttl($key);\n\n $this->info(\"Redis key: {$key}\");\n $this->info('Value: ' . ($value ?? '(empty)'));\n $this->info(\"TTL: {$ttl}s\");\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Console\\Commands;\n\nuse Carbon\\Carbon;\nuse Carbon\\CarbonImmutable;\nuse Illuminate\\Console\\Command;\nuse Illuminate\\Support\\Facades\\Redis;\nuse InvalidArgumentException;\nuse Jiminny\\Jobs\\AutomatedReports\\RequestGenerateAskJiminnyReportJob;\nuse Jiminny\\Jobs\\AutomatedReports\\SendReportMailJob;\nuse Jiminny\\Jobs\\Crm\\Delete\\VerifyActivityCrmTaskJob;\nuse Jiminny\\Jobs\\Crm\\MatchActivityCrmData;\nuse Jiminny\\Jobs\\JobDispatcherInterface;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\AutomatedReportResult;\nuse Jiminny\\Models\\Team;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Repositories\\AutomatedReportsRepository;\nuse Jiminny\\Services\\Activity\\CrmOwnerResolver;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\n\n/**\n * Class JiminnyDebugCommand\n *\n * @package Jiminny\\Console\\Commands\n */\nclass JiminnyDebugCommand extends Command\n{\n public const string FREQUENCY_DAILY = 'daily';\n public const string FREQUENCY_WEEKLY = 'weekly';\n public const string FREQUENCY_MONTHLY = 'monthly';\n public const string FREQUENCY_QUARTERLY = 'quarterly';\n public const string FREQUENCY_ONE_OFF = 'one_off';\n protected $signature = 'jiminny:debug';\n\n public function handle(\n JobDispatcherInterface $jobDispatcher,\n AutomatedReportsService $automatedReportsService,\n AutomatedReportsRepository $automatedReportsRepository,\n UserPilotClient $userPilotClient\n ): void {\n // Choose ONE of the following to run, then comment out the others.\n // 1) Dispatch a storm of MatchActivityCrmData jobs against team 2\n $this->simulateMatchActivityStorm(teamId: 2, count: 100);\n\n // 2) Dispatch a storm of VerifyActivityCrmTaskJob jobs (simulates DeleteCrmEntityTrait fan-out)\n // $this->simulateVerifyTaskStorm(teamId: 2, count: 100);\n\n // 3) Inspect Redis circuit-breaker state for the team's HubSpot portal\n // $this->observeRateLimitCache(teamId: 2);\n\n // 4) Make 3 synchronous matchByName calls (foreground, hits API directly)\n // $this->rateLimit();\n exit(1);\n\n\n\n $report = AutomatedReport::find(71);\n $last = AutomatedReportResult::query()\n ->where('report_id', $report->getId())\n ->whereIn('status', [AutomatedReportResult::STATUS_DEFAULT, AutomatedReportResult::STATUS_FAILED])\n// ->where('reason', '!=', AutomatedReportResult::REASON_NOT_ENOUGH_ACTIVITIES)\n ->whereDate('created_at', CarbonImmutable::now()->toDateString())\n ->latest()\n ->first();\n\n $this->info(\"Last: {$last->getId()}\");\n\n exit(1);\n\n $user = User::find(143);\n // $count = $automatedReportsRepository->countUserReports($user);\n // $this->info(\"Count: {$count}\");\n // $count = $automatedReportsRepository->countAllUserReports($user);\n // $this->info(\"All count: {$count}\");\n\n $payload = [\n 'report_type' => 'ask_jiminny',\n 'frequency' => 'weekly',\n ];\n $userPilotClient->track($user, 'ask-jiminny-report-generated', $payload);\n\n exit(1);\n\n $now = Carbon::now()->subDay(1);\n $this->info(\"Now: {$now->toDateTimeString()}\");\n $weekStart = Carbon::getWeekStartsAt();\n $this->info(\"Now: {$weekStart}\");\n\n // $from = $now->copy()->previousWeekday()->startOfDay();\n // $to = $now->copy()->previousWeekday()->endOfDay();\n\n // $fromOld = $now->copy()->subWeeks(1)->startOfDay();\n // $toOld = $now->copy()->subDay()->endOfDay();\n // $fromNew = $now->copy()->subWeek()->startOfWeek();\n // $toNew = $now->copy()->subWeek()->endOfWeek();\n\n // $fromOld = $now->copy()->subMonths(1)->startOfDay();\n // $toOld = $now->copy()->subDay()->endOfDay();\n // $fromNew = $now->copy()->subMonthNoOverflow()->startOfMonth();\n // $toNew = $now->copy()->subMonthNoOverflow()->endOfMonth();\n\n $fromOld = $now->copy()->subMonths(3)->startOfDay();\n $toOld = $now->copy()->subDay()->endOfDay();\n $fromNew = $now->copy()->subQuarterNoOverflow()->startOfQuarter();\n $toNew = $now->copy()->subQuarterNoOverflow()->endOfQuarter();\n\n $this->info(\"From old: {$fromOld->toDateTimeString()}\");\n $this->info(\"To old: {$toOld->toDateTimeString()}\");\n $this->info(\"From new: {$fromNew->toDateTimeString()}\");\n $this->info(\"To new: {$toNew->toDateTimeString()}\");\n\n exit(1);\n\n $report = AutomatedReport::find(71);\n\n $job = new RequestGenerateAskJiminnyReportJob($report->getUuid());\n $jobDispatcher->dispatch($job);\n\n exit(1);\n\n\n // $this->formatDate($jobDispatcher);\n // $this->sendMail($jobDispatcher, $automatedReportsService);\n // $this->crmService();\n\n $this->getPayload($automatedReportsService);\n\n exit(1);\n }\n\n\n\n private function crmService()\n {\n $activity = Activity::find(418141);\n\n $team = Team::find(19);\n $config = $team->getCrmConfiguration();\n\n $crmResolver = app(CrmOwnerResolver::class, [\n 'team' => $team,\n 'integrationAdmin' => $team->getOwner(),\n 'providerSlug' => $config->getProviderName(),\n ]);\n\n $crmService = $crmResolver->prepareCrmService();\n\n $crmService->createTranscriptNotes($activity);\n }\n\n private function sendMail(JobDispatcherInterface $jobDispatcher, AutomatedReportsService $automatedReportsService)\n {\n $reportUuid = '';\n // $report = $automatedReportsService->getReportResult($reportUuid);\n $report = AutomatedReportResult::find(275);\n $validRecipients = $automatedReportsService->getValidRecipientUsers(\n $report->getReport(),\n includeJiminny: true,\n );\n\n $recipient = $validRecipients[0];\n\n $fileName = $automatedReportsService->getReportFileName($report);\n $typeName = $report->getReport()->getCustomName()\n ?? $automatedReportsService->getReportTypeName($report);\n $teamsName = $automatedReportsService->getReportTeamsName($report);\n $periodName = $automatedReportsService->getReportPeriodName($report);\n $s3Path = $automatedReportsService->getMediaPath($report);\n\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$fileName ' . PHP_EOL . print_r($fileName, true));\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$typeName ' . PHP_EOL . print_r($typeName, true));\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$teamsName ' . PHP_EOL . print_r($teamsName, true));\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$periodName ' . PHP_EOL . print_r($periodName, true));\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$s3Path ' . PHP_EOL . print_r($s3Path, true));\n\n $jobDispatcher->dispatch(\n new SendReportMailJob(\n reportUuid: $report->getUuid(),\n s3Path: $s3Path,\n recipientEmail: $recipient['email'],\n recipientName: $recipient['name'] ?? null,\n fileName: $fileName,\n typeName: $typeName,\n teamsName: $teamsName,\n periodName: $periodName,\n isAskJiminny: true,\n )\n );\n\n exit(1);\n }\n\n private function formatDate(JobDispatcherInterface $jobDispatcher): void\n {\n $customName = 'Custom report name';\n // $frequency = self::FREQUENCY_DAILY;\n // $frequency = self::FREQUENCY_WEEKLY;\n $frequency = self::FREQUENCY_MONTHLY;\n // $frequency = self::FREQUENCY_QUARTERLY;\n // $frequency = self::FREQUENCY_ONE_OFF;\n $period = $this->calculateFromAndToDatePeriod($frequency);\n $from = $period['fromDate'];\n $to = $period['toDate'];\n $periodName = $this->formatReportPeriodName($frequency, $from, $to);\n $filenameSuffix = null;\n\n if ($customName) {\n if ($filenameSuffix) {\n $customName .= \" {$filenameSuffix}\";\n }\n\n $result = $this->sanitizeFileName(\"{$customName} - {$periodName}\");\n }\n\n $this->info($result);\n }\n\n public function calculateFromAndToDatePeriod(\n string $frequency,\n ?Carbon $fromDate = null,\n ?Carbon $toDate = null\n ): array {\n if ($frequency === self::FREQUENCY_ONE_OFF) {\n return [\n 'fromDate' => $fromDate,\n 'toDate' => $toDate,\n ];\n }\n\n $now = Carbon::now();\n\n return match ($frequency) {\n self::FREQUENCY_DAILY => [\n 'fromDate' => $now->copy()->subDay()->startOfDay(),\n 'toDate' => $now->copy()->subDay()->endOfDay(),\n ],\n self::FREQUENCY_WEEKLY => [\n 'fromDate' => $now->copy()->subWeeks(1)->startOfDay(),\n 'toDate' => $now->copy()->subDay()->endOfDay(),\n ],\n self::FREQUENCY_MONTHLY => [\n 'fromDate' => $now->copy()->subMonths(1)->startOfDay(),\n 'toDate' => $now->copy()->subDay()->endOfDay(),\n ],\n self::FREQUENCY_QUARTERLY => [\n 'fromDate' => $now->copy()->subMonths(3)->startOfDay(),\n 'toDate' => $now->copy()->subDay()->endOfDay(),\n ],\n default => throw new InvalidArgumentException(\"Unsupported frequency: {$frequency}\"),\n };\n }\n\n private function formatReportPeriodName(string $frequency, Carbon $from, Carbon $to): string\n {\n $fromYear = $from->format('Y');\n $toYear = $to->format('Y');\n $differentYears = $fromYear !== $toYear;\n\n switch ($frequency) {\n case self::FREQUENCY_DAILY:\n return $from->format('j M Y');\n\n case self::FREQUENCY_QUARTERLY:\n // 'Jan-Mar 2025' or 'Nov 2024-Jan 2025' if years differ\n $startMonth = $from->format('M');\n $endMonth = $to->copy()->subMonth();\n $endMonthName = $endMonth->format('M');\n $endMonthYear = $endMonth->format('Y');\n\n if ($differentYears) {\n return \"{$startMonth} {$fromYear} - {$endMonthName} {$endMonthYear}\";\n }\n\n return \"{$startMonth} - {$endMonthName} {$toYear}\";\n\n case self::FREQUENCY_MONTHLY:\n // 'May 2025' - monthly reports are always within the same year\n return $from->format('M Y');\n\n case self::FREQUENCY_WEEKLY:\n // '4 - 8 Aug 2025', '27 Oct - 3 Nov 2025', or '28 Dec 2024 - 3 Jan 2025' if years differ\n $startDay = $from->format('j');\n $endDay = $to->format('j');\n $startMonth = $from->format('M');\n $endMonth = $to->format('M');\n\n if ($differentYears) {\n return \"{$startDay} {$startMonth} {$fromYear} - {$endDay} {$endMonth} {$toYear}\";\n }\n\n if ($startMonth !== $endMonth) {\n return \"{$startDay} {$startMonth} - {$endDay} {$endMonth} {$toYear}\";\n }\n\n return \"{$startDay} - {$endDay} {$endMonth} {$toYear}\";\n\n case self::FREQUENCY_ONE_OFF:\n // '2 May-31 May 2025' or '15 Dec 2024-15 Jan 2025' if years differ\n $startDay = $from->format('j');\n $startMonth = $from->format('M');\n $endDay = $to->format('j');\n $endMonth = $to->format('M');\n\n // If same month and year, use a format like '2-31 May 2025'\n if ($startMonth === $endMonth && ! $differentYears) {\n return \"{$startDay} - {$endDay} {$startMonth} {$toYear}\";\n }\n\n // If different years, include both years\n if ($differentYears) {\n return \"{$startDay} {$startMonth} {$fromYear} - {$endDay} {$endMonth} {$toYear}\";\n }\n\n // Same year but different months\n return \"{$startDay} {$startMonth} - {$endDay} {$endMonth} {$toYear}\";\n\n default:\n // Default format for unknown frequencies\n return $from->format('j M Y') . ' - ' . $to->format('j M Y');\n }\n }\n\n public function sanitizeFileName(string $fileName): string\n {\n return str_replace(['/', '\\\\'], '-', $fileName);\n }\n\n private function getPayload(AutomatedReportsService $automatedReportsService)\n {\n $reportResult = AutomatedReportResult::find(269);\n $automatedReport = $reportResult->getReport();\n $activityIds = [1,2,3];\n $payload = $automatedReportsService->getAskJiminnyGenerateReportPayload(\n automatedReport: $automatedReport,\n reportResult: $reportResult,\n activityIds: $activityIds,\n );\n\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$payload ' . PHP_EOL . print_r($payload, true));\n }\n\n private function rateLimit()\n {\n $team = Team::find(2);\n $config = $team->getCrmConfiguration();\n\n $crmResolver = app(CrmOwnerResolver::class, [\n 'team' => $team,\n 'integrationAdmin' => $team->getOwner(),\n 'providerSlug' => $config->getProviderName(),\n ]);\n\n $crmService = $crmResolver->prepareCrmService();\n\n for ($i = 0 ; $i < 3; $i++) {\n// if ($i % 25 === 0) {\n// $this->info(\"Syncing opportunity {$i}\");\n $this->info(\"Matching contact {$i}\");\n// }\n// $crmService->syncOpportunity('374720564');\n $crmService->matchByName('Robot');\n }\n }\n\n private function simulateMatchActivityStorm(int $teamId = 2, int $count = 100): void\n {\n $team = Team::find($teamId);\n $config = $team->getCrmConfiguration();\n\n $activities = Activity::query()\n ->where('crm_configuration_id', $config->getId())\n ->orderByDesc('id')\n ->limit($count)\n ->get();\n\n $this->info(\"Dispatching {$activities->count()} MatchActivityCrmData jobs (portal={$config->getId()})\");\n\n foreach ($activities as $activity) {\n MatchActivityCrmData::dispatch($activity->getId(), $config, true);\n }\n\n $this->info('Done. Watch logs and run jiminny:debug observeRateLimit to inspect cache state.');\n }\n\n private function simulateVerifyTaskStorm(int $teamId = 2, int $count = 100): void\n {\n $activities = Activity::query()\n ->where('team_id', $teamId)\n ->whereNotNull('crm_provider_id')\n ->orderByDesc('id')\n ->limit($count)\n ->get();\n\n $this->info(\"Dispatching {$activities->count()} VerifyActivityCrmTaskJob jobs\");\n\n foreach ($activities as $activity) {\n VerifyActivityCrmTaskJob::dispatch($activity->getId());\n }\n\n $this->info('Done.');\n }\n\n private function observeRateLimitCache(int $teamId = 2): void\n {\n $team = Team::find($teamId);\n $config = $team->getCrmConfiguration();\n $key = sprintf('hubspot:ratelimit:portal:%d', $config->getId());\n\n $value = Redis::get($key);\n $ttl = Redis::ttl($key);\n\n $this->info(\"Redis key: {$key}\");\n $this->info('Value: ' . ($value ?? '(empty)'));\n $this->info(\"TTL: {$ttl}s\");\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Show Replace Field","depth":4,"bounds":{"left":0.5575133,"top":0.08060654,"width":0.008643617,"height":0.01915403},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Search History","depth":3,"bounds":{"left":0.57014626,"top":0.07980846,"width":0.00731383,"height":0.017557861},"on_screen":true,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
-113513835708095557
|
3603276677944781227
|
click
|
accessibility
|
NULL
|
Project: faVsco.js, menu
JY-20725-handle-HS-search Project: faVsco.js, menu
JY-20725-handle-HS-search-rate-limit, menu
Start Listening for PHP Debug Connections
HandleHubspotRateLimitTest
Run 'HandleHubspotRateLimitTest'
Debug 'HandleHubspotRateLimitTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
5
133
11
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Console\Commands;
use Carbon\Carbon;
use Carbon\CarbonImmutable;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Redis;
use InvalidArgumentException;
use Jiminny\Jobs\AutomatedReports\RequestGenerateAskJiminnyReportJob;
use Jiminny\Jobs\AutomatedReports\SendReportMailJob;
use Jiminny\Jobs\Crm\Delete\VerifyActivityCrmTaskJob;
use Jiminny\Jobs\Crm\MatchActivityCrmData;
use Jiminny\Jobs\JobDispatcherInterface;
use Jiminny\Models\Activity;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\AutomatedReportResult;
use Jiminny\Models\Team;
use Jiminny\Models\User;
use Jiminny\Repositories\AutomatedReportsRepository;
use Jiminny\Services\Activity\CrmOwnerResolver;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
/**
* Class JiminnyDebugCommand
*
* @package Jiminny\Console\Commands
*/
class JiminnyDebugCommand extends Command
{
public const string FREQUENCY_DAILY = 'daily';
public const string FREQUENCY_WEEKLY = 'weekly';
public const string FREQUENCY_MONTHLY = 'monthly';
public const string FREQUENCY_QUARTERLY = 'quarterly';
public const string FREQUENCY_ONE_OFF = 'one_off';
protected $signature = 'jiminny:debug';
public function handle(
JobDispatcherInterface $jobDispatcher,
AutomatedReportsService $automatedReportsService,
AutomatedReportsRepository $automatedReportsRepository,
UserPilotClient $userPilotClient
): void {
// Choose ONE of the following to run, then comment out the others.
// 1) Dispatch a storm of MatchActivityCrmData jobs against team 2
$this->simulateMatchActivityStorm(teamId: 2, count: 100);
// 2) Dispatch a storm of VerifyActivityCrmTaskJob jobs (simulates DeleteCrmEntityTrait fan-out)
// $this->simulateVerifyTaskStorm(teamId: 2, count: 100);
// 3) Inspect Redis circuit-breaker state for the team's HubSpot portal
// $this->observeRateLimitCache(teamId: 2);
// 4) Make 3 synchronous matchByName calls (foreground, hits API directly)
// $this->rateLimit();
exit(1);
$report = AutomatedReport::find(71);
$last = AutomatedReportResult::query()
->where('report_id', $report->getId())
->whereIn('status', [AutomatedReportResult::STATUS_DEFAULT, AutomatedReportResult::STATUS_FAILED])
// ->where('reason', '!=', AutomatedReportResult::REASON_NOT_ENOUGH_ACTIVITIES)
->whereDate('created_at', CarbonImmutable::now()->toDateString())
->latest()
->first();
$this->info("Last: {$last->getId()}");
exit(1);
$user = User::find(143);
// $count = $automatedReportsRepository->countUserReports($user);
// $this->info("Count: {$count}");
// $count = $automatedReportsRepository->countAllUserReports($user);
// $this->info("All count: {$count}");
$payload = [
'report_type' => 'ask_jiminny',
'frequency' => 'weekly',
];
$userPilotClient->track($user, 'ask-jiminny-report-generated', $payload);
exit(1);
$now = Carbon::now()->subDay(1);
$this->info("Now: {$now->toDateTimeString()}");
$weekStart = Carbon::getWeekStartsAt();
$this->info("Now: {$weekStart}");
// $from = $now->copy()->previousWeekday()->startOfDay();
// $to = $now->copy()->previousWeekday()->endOfDay();
// $fromOld = $now->copy()->subWeeks(1)->startOfDay();
// $toOld = $now->copy()->subDay()->endOfDay();
// $fromNew = $now->copy()->subWeek()->startOfWeek();
// $toNew = $now->copy()->subWeek()->endOfWeek();
// $fromOld = $now->copy()->subMonths(1)->startOfDay();
// $toOld = $now->copy()->subDay()->endOfDay();
// $fromNew = $now->copy()->subMonthNoOverflow()->startOfMonth();
// $toNew = $now->copy()->subMonthNoOverflow()->endOfMonth();
$fromOld = $now->copy()->subMonths(3)->startOfDay();
$toOld = $now->copy()->subDay()->endOfDay();
$fromNew = $now->copy()->subQuarterNoOverflow()->startOfQuarter();
$toNew = $now->copy()->subQuarterNoOverflow()->endOfQuarter();
$this->info("From old: {$fromOld->toDateTimeString()}");
$this->info("To old: {$toOld->toDateTimeString()}");
$this->info("From new: {$fromNew->toDateTimeString()}");
$this->info("To new: {$toNew->toDateTimeString()}");
exit(1);
$report = AutomatedReport::find(71);
$job = new RequestGenerateAskJiminnyReportJob($report->getUuid());
$jobDispatcher->dispatch($job);
exit(1);
// $this->formatDate($jobDispatcher);
// $this->sendMail($jobDispatcher, $automatedReportsService);
// $this->crmService();
$this->getPayload($automatedReportsService);
exit(1);
}
private function crmService()
{
$activity = Activity::find(418141);
$team = Team::find(19);
$config = $team->getCrmConfiguration();
$crmResolver = app(CrmOwnerResolver::class, [
'team' => $team,
'integrationAdmin' => $team->getOwner(),
'providerSlug' => $config->getProviderName(),
]);
$crmService = $crmResolver->prepareCrmService();
$crmService->createTranscriptNotes($activity);
}
private function sendMail(JobDispatcherInterface $jobDispatcher, AutomatedReportsService $automatedReportsService)
{
$reportUuid = '';
// $report = $automatedReportsService->getReportResult($reportUuid);
$report = AutomatedReportResult::find(275);
$validRecipients = $automatedReportsService->getValidRecipientUsers(
$report->getReport(),
includeJiminny: true,
);
$recipient = $validRecipients[0];
$fileName = $automatedReportsService->getReportFileName($report);
$typeName = $report->getReport()->getCustomName()
?? $automatedReportsService->getReportTypeName($report);
$teamsName = $automatedReportsService->getReportTeamsName($report);
$periodName = $automatedReportsService->getReportPeriodName($report);
$s3Path = $automatedReportsService->getMediaPath($report);
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$fileName ' . PHP_EOL . print_r($fileName, true));
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$typeName ' . PHP_EOL . print_r($typeName, true));
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$teamsName ' . PHP_EOL . print_r($teamsName, true));
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$periodName ' . PHP_EOL . print_r($periodName, true));
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$s3Path ' . PHP_EOL . print_r($s3Path, true));
$jobDispatcher->dispatch(
new SendReportMailJob(
reportUuid: $report->getUuid(),
s3Path: $s3Path,
recipientEmail: $recipient['email'],
recipientName: $recipient['name'] ?? null,
fileName: $fileName,
typeName: $typeName,
teamsName: $teamsName,
periodName: $periodName,
isAskJiminny: true,
)
);
exit(1);
}
private function formatDate(JobDispatcherInterface $jobDispatcher): void
{
$customName = 'Custom report name';
// $frequency = self::FREQUENCY_DAILY;
// $frequency = self::FREQUENCY_WEEKLY;
$frequency = self::FREQUENCY_MONTHLY;
// $frequency = self::FREQUENCY_QUARTERLY;
// $frequency = self::FREQUENCY_ONE_OFF;
$period = $this->calculateFromAndToDatePeriod($frequency);
$from = $period['fromDate'];
$to = $period['toDate'];
$periodName = $this->formatReportPeriodName($frequency, $from, $to);
$filenameSuffix = null;
if ($customName) {
if ($filenameSuffix) {
$customName .= " {$filenameSuffix}";
}
$result = $this->sanitizeFileName("{$customName} - {$periodName}");
}
$this->info($result);
}
public function calculateFromAndToDatePeriod(
string $frequency,
?Carbon $fromDate = null,
?Carbon $toDate = null
): array {
if ($frequency === self::FREQUENCY_ONE_OFF) {
return [
'fromDate' => $fromDate,
'toDate' => $toDate,
];
}
$now = Carbon::now();
return match ($frequency) {
self::FREQUENCY_DAILY => [
'fromDate' => $now->copy()->subDay()->startOfDay(),
'toDate' => $now->copy()->subDay()->endOfDay(),
],
self::FREQUENCY_WEEKLY => [
'fromDate' => $now->copy()->subWeeks(1)->startOfDay(),
'toDate' => $now->copy()->subDay()->endOfDay(),
],
self::FREQUENCY_MONTHLY => [
'fromDate' => $now->copy()->subMonths(1)->startOfDay(),
'toDate' => $now->copy()->subDay()->endOfDay(),
],
self::FREQUENCY_QUARTERLY => [
'fromDate' => $now->copy()->subMonths(3)->startOfDay(),
'toDate' => $now->copy()->subDay()->endOfDay(),
],
default => throw new InvalidArgumentException("Unsupported frequency: {$frequency}"),
};
}
private function formatReportPeriodName(string $frequency, Carbon $from, Carbon $to): string
{
$fromYear = $from->format('Y');
$toYear = $to->format('Y');
$differentYears = $fromYear !== $toYear;
switch ($frequency) {
case self::FREQUENCY_DAILY:
return $from->format('j M Y');
case self::FREQUENCY_QUARTERLY:
// 'Jan-Mar 2025' or 'Nov 2024-Jan 2025' if years differ
$startMonth = $from->format('M');
$endMonth = $to->copy()->subMonth();
$endMonthName = $endMonth->format('M');
$endMonthYear = $endMonth->format('Y');
if ($differentYears) {
return "{$startMonth} {$fromYear} - {$endMonthName} {$endMonthYear}";
}
return "{$startMonth} - {$endMonthName} {$toYear}";
case self::FREQUENCY_MONTHLY:
// 'May 2025' - monthly reports are always within the same year
return $from->format('M Y');
case self::FREQUENCY_WEEKLY:
// '4 - 8 Aug 2025', '27 Oct - 3 Nov 2025', or '28 Dec 2024 - 3 Jan 2025' if years differ
$startDay = $from->format('j');
$endDay = $to->format('j');
$startMonth = $from->format('M');
$endMonth = $to->format('M');
if ($differentYears) {
return "{$startDay} {$startMonth} {$fromYear} - {$endDay} {$endMonth} {$toYear}";
}
if ($startMonth !== $endMonth) {
return "{$startDay} {$startMonth} - {$endDay} {$endMonth} {$toYear}";
}
return "{$startDay} - {$endDay} {$endMonth} {$toYear}";
case self::FREQUENCY_ONE_OFF:
// '2 May-31 May 2025' or '15 Dec 2024-15 Jan 2025' if years differ
$startDay = $from->format('j');
$startMonth = $from->format('M');
$endDay = $to->format('j');
$endMonth = $to->format('M');
// If same month and year, use a format like '2-31 May 2025'
if ($startMonth === $endMonth && ! $differentYears) {
return "{$startDay} - {$endDay} {$startMonth} {$toYear}";
}
// If different years, include both years
if ($differentYears) {
return "{$startDay} {$startMonth} {$fromYear} - {$endDay} {$endMonth} {$toYear}";
}
// Same year but different months
return "{$startDay} {$startMonth} - {$endDay} {$endMonth} {$toYear}";
default:
// Default format for unknown frequencies
return $from->format('j M Y') . ' - ' . $to->format('j M Y');
}
}
public function sanitizeFileName(string $fileName): string
{
return str_replace(['/', '\\'], '-', $fileName);
}
private function getPayload(AutomatedReportsService $automatedReportsService)
{
$reportResult = AutomatedReportResult::find(269);
$automatedReport = $reportResult->getReport();
$activityIds = [1,2,3];
$payload = $automatedReportsService->getAskJiminnyGenerateReportPayload(
automatedReport: $automatedReport,
reportResult: $reportResult,
activityIds: $activityIds,
);
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$payload ' . PHP_EOL . print_r($payload, true));
}
private function rateLimit()
{
$team = Team::find(2);
$config = $team->getCrmConfiguration();
$crmResolver = app(CrmOwnerResolver::class, [
'team' => $team,
'integrationAdmin' => $team->getOwner(),
'providerSlug' => $config->getProviderName(),
]);
$crmService = $crmResolver->prepareCrmService();
for ($i = 0 ; $i < 3; $i++) {
// if ($i % 25 === 0) {
// $this->info("Syncing opportunity {$i}");
$this->info("Matching contact {$i}");
// }
// $crmService->syncOpportunity('374720564');
$crmService->matchByName('Robot');
}
}
private function simulateMatchActivityStorm(int $teamId = 2, int $count = 100): void
{
$team = Team::find($teamId);
$config = $team->getCrmConfiguration();
$activities = Activity::query()
->where('crm_configuration_id', $config->getId())
->orderByDesc('id')
->limit($count)
->get();
$this->info("Dispatching {$activities->count()} MatchActivityCrmData jobs (portal={$config->getId()})");
foreach ($activities as $activity) {
MatchActivityCrmData::dispatch($activity->getId(), $config, true);
}
$this->info('Done. Watch logs and run jiminny:debug observeRateLimit to inspect cache state.');
}
private function simulateVerifyTaskStorm(int $teamId = 2, int $count = 100): void
{
$activities = Activity::query()
->where('team_id', $teamId)
->whereNotNull('crm_provider_id')
->orderByDesc('id')
->limit($count)
->get();
$this->info("Dispatching {$activities->count()} VerifyActivityCrmTaskJob jobs");
foreach ($activities as $activity) {
VerifyActivityCrmTaskJob::dispatch($activity->getId());
}
$this->info('Done.');
}
private function observeRateLimitCache(int $teamId = 2): void
{
$team = Team::find($teamId);
$config = $team->getCrmConfiguration();
$key = sprintf('hubspot:ratelimit:portal:%d', $config->getId());
$value = Redis::get($key);
$ttl = Redis::ttl($key);
$this->info("Redis key: {$key}");
$this->info('Value: ' . ($value ?? '(empty)'));
$this->info("TTL: {$ttl}s");
}
}
Show Replace Field
Search History...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
18235
|
790
|
23
|
2026-05-11T11:05:54.283544+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-11/1778 /Users/lukas/.screenpipe/data/data/2026-05-11/1778497554283_m1.jpg...
|
PhpStorm
|
faVsco.js – laravel.log
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
JY-20725-handle-HS-search Project: faVsco.js, menu
JY-20725-handle-HS-search-rate-limit, menu
Start Listening for PHP Debug Connections
HandleHubspotRateLimitTest
Run 'HandleHubspotRateLimitTest'
Debug 'HandleHubspotRateLimitTest'...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JY-20725-handle-HS-search-rate-limit, menu","depth":5,"on_screen":true,"help_text":"Git Branch: JY-20725-handle-HS-search-rate-limit","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"HandleHubspotRateLimitTest","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'HandleHubspotRateLimitTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'HandleHubspotRateLimitTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
-5870622292910750321
|
-8348263803913827968
|
click
|
hybrid
|
NULL
|
Project: faVsco.js, menu
JY-20725-handle-HS-search Project: faVsco.js, menu
JY-20725-handle-HS-search-rate-limit, menu
Start Listening for PHP Debug Connections
HandleHubspotRateLimitTest
Run 'HandleHubspotRateLimitTest'
Debug 'HandleHubspotRateLimitTest'
SlackFileEditViewGoHistoryWindowHelp• 0DOCKERcompiledeventsroutesviewsO ₴1DEV (docker)$2APP (-zsh)883Jiminny-worker-processing-delayed: jiminny-worker-processing-delayed_00:stoppedjiminny-worker-processing-2:jiminny-worker-processing-2_00: stoppedjiminny-worker-processing-3:jiminny-worker-processing-3_00: stoppedjiminny-worker-processing-4:jiminny-worker-processing-4_00:stoppedjiminny-worker-processing-5: jiminny-worker-processing-5_00:stoppedworker-analytics:worker-analytics_00: stoppedworker-crm-update:worker-crm-update_00: stoppedworker-download:worker-download_00:stoppedworker-nudges:worker-nudges_00:stoppedjiminny-worker-processing-1:jiminny-worker-processing-1_00: stoppedworker:worker_00: stoppedworker-calendar:worker-calendar_00:worker-conferences:worker-conferences_00:worker-crm-sync:worker-crm-sync_00: stoppedworker-es-update:worker-es-update_00:worker-emails:worker-emails_00: stoppedworker-audio:worker-audio_00: stoppedartisan-schedule:artisan-schedule_00: stoppedartisan-schedule:artisan-schedule_00: startedjiminny-worker-processing-1:jiminny-worker-processing-1_00: startedjiminny-worker-processing-2:jiminny-worker-processing-2_00: startedjiminny-worker-processing-3:jiminny-worker-processing-3_00: startedjiminny-worker-processing-4:jiminny-worker-processing-4_00: startedjiminny-worker-processing-5:jiminny-worker-processing-5_00:startedjiminny-worker-processing-delayed: jiminny-worker-processing-delayed_00: startedworker:worker_00: startedworker-analytics:worker-analytics_00: startedworker-audio:worker-audio_00: startedworker-calendar:worker-calendar_00:startedworker-conferences:worker-conferences_00: startedworker-crm-sync:worker-crm-sync_00: startedworker-crm-update:worker-crm-update_00: startedworker-download:worker-download_00: startedworker-emails:worker-emails_00: startedworker-es-update:worker-es-update_00:startedworker-nudges:worker-nudges_00: startedroot@docker_lamp_1:/home/jiminny#php artisan jiminny:debugDispatching 100 MatchActivityCrmDatajobs (portal=2)Done.Watch logs and runjiminny:debug observeRateLimit to inspect cache state.root@docker_lamp_1:/home/jiminny# ]DEV (dl-zshHomeDMsActivityFilesLater...Morealo)• Preparation for Refi... in 55 m100% <78• Mon 11 May 14:05:53→Describe what you are looking forJiminny ...# contusion-clinic# curiosity_lab# engineering# general# jiminny-bg# platform-tickets# product_launches# random# releases# sofia-office# support# thank-yous# the_people_of jimi...^ Direct messagesP. Aneliya Angelova®. Galya Dimitrova& Petko Kashinski&. Stefka StoyanovaVasil Vasilev&. Nikolay IvanovAneliya Angelova, ...Stoyan Tanev• VesE Lukas Kovalik y…..::: AppsS Jira CloudToastGanala Cala# thank-yous406 0Messagest* Add canvas+O FilesWednesday, May 6th~It shows increaiuie skir tu jump into Supportand not miss a beat.More importantly, itshows great heart and helped delight ourcustomers on two ways at the same time:Support and Trials.You embodied so many of our values duringthis period.#selflessness #lowego #customerfocus#positiveattitudeView Recognition Moment05WB1011 reply 5 days agoToday ~Greg 1:31 PMShout out to engineering/infrastructure teamand their stability work. Did you know ... last weekour Product had 100% uptime? ..not99.9999999% but /2 %!lots of little things (and a few big things) make thathappen. well done all!81#1NewKara Jones 1:54 PM100!!!!!! That is amazing!!! Power team!!Message #thank-yous+...
|
18231
|
NULL
|
NULL
|
NULL
|
|
18236
|
790
|
24
|
2026-05-11T11:06:06.547070+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-11/1778 /Users/lukas/.screenpipe/data/data/2026-05-11/1778497566547_m1.jpg...
|
PhpStorm
|
faVsco.js – laravel.log
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
JY-20725-handle-HS-search Project: faVsco.js, menu
JY-20725-handle-HS-search-rate-limit, menu
Start Listening for PHP Debug Connections
HandleHubspotRateLimitTest
Run 'HandleHubspotRateLimitTest'
Debug 'HandleHubspotRateLimitTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JY-20725-handle-HS-search-rate-limit, menu","depth":5,"on_screen":true,"help_text":"Git Branch: JY-20725-handle-HS-search-rate-limit","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"HandleHubspotRateLimitTest","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'HandleHubspotRateLimitTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'HandleHubspotRateLimitTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
1579713784452094674
|
-8780890023316608054
|
click
|
hybrid
|
NULL
|
Project: faVsco.js, menu
JY-20725-handle-HS-search Project: faVsco.js, menu
JY-20725-handle-HS-search-rate-limit, menu
Start Listening for PHP Debug Connections
HandleHubspotRateLimitTest
Run 'HandleHubspotRateLimitTest'
Debug 'HandleHubspotRateLimitTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
SlackFileEditViewGoHistoryWindowHelp• 0DOCKERcompiledeventsroutesviews- ₴81DEV (docker)$2APP (-zsh)883Jiminny-worker-processing-delayed: jiminny-worker-processing-delayed_00:stoppedjiminny-worker-processing-2:jiminny-worker-processing-2_00: stoppedjiminny-worker-processing-3:jiminny-worker-processing-3_00: stoppedjiminny-worker-processing-4:jiminny-worker-processing-4_00:jiminny-worker-processing-5: jiminny-worker-processing-5_00:stoppedstoppedworker-analytics:worker-analytics_00: stoppedworker-crm-update:worker-crm-update_00: stoppedworker-download:worker-download_00:stoppedworker-nudges:worker-nudges_00:stoppedjiminny-worker-processing-1:jiminny-worker-processing-1_00: stoppedworker:worker_00: stoppedworker-calendar:worker-calendar_00:worker-conferences:worker-conferences_00:worker-crm-sync:worker-crm-sync_00: stoppedworker-es-update:worker-es-update_00:worker-emails:worker-emails_00: stoppedworker-audio:worker-audio_00: stoppedartisan-schedule:artisan-schedule_00: stoppedartisan-schedule:artisan-schedule_00: startedjiminny-worker-processing-1:jiminny-worker-processing-1_00: startedjiminny-worker-processing-2:jiminny-worker-processing-2_00: startedjiminny-worker-processing-3:jiminny-worker-processing-3_00: startedjiminny-worker-processing-4:jiminny-worker-processing-4_00: startedjiminny-worker-processing-5:jiminny-worker-processing-5_00:startedjiminny-worker-processing-delayed: jiminny-worker-processing-delayed_00: startedworker:worker_00: startedworker-analytics:worker-analytics_00: startedworker-audio:worker-audio_00: startedworker-calendar:worker-calendar_00:startedworker-conferences:worker-conferences_00: startedworker-crm-sync:worker-crm-sync_00: startedworker-crm-update:worker-crm-update_00: startedworker-download:worker-download_00: startedworker-emails:worker-emails_00: startedworker-es-update:worker-es-update_00:startedworker-nudges:worker-nudges_00: startedroot@docker_lamp_1:/home/jiminny#php artisan jiminny:debugDispatching 100 MatchActivityCrmDatajobs (portal=2)Done.Watch logs and runjiminny:debug observeRateLimit to inspect cache state.root@docker_lamp_1:/home/jiminny# ]DEV (dl-zshHomeDMsActivityFilesLater...Morealo)• Preparation for Refi... in 54 m100% <78• Mon 11 May 14:06:06→Describe what you are looking forJiminny ...# contusion-clinic# curiosity_lab# engineering# general# jiminny-bg# platform-tickets# product_launches# random# releases# sofia-office# support# thank-yous# the_people_of jimi...^ Direct messagesP. Aneliya Angelova®. Galya Dimitrova& Petko Kashinski&. Stefka StoyanovaVasil Vasilev&. Nikolay IvanovAneliya Angelova, ...Stoyan Tanev. VesE Lukas Kovalik y…..::: AppsS Jira CloudToastGanala Cala# thank-yous406 0Messagest* Add canvas+O FilesWednesday, May 6th~It shows increaiuie skir tu jump into Supportand not miss a beat.More importantly, itshows great heart and helped delight ourcustomers on two ways at the same time:Support and Trials.You embodied so many of our values duringthis period.#selflessness #lowego #customerfocus#positiveattitudeView Recognition MomentW1011 reply 5 days agoToday ~Greg 1:31 PMShout out to engineering/infrastructure teamand their stability work. Did you know ... last weekour Product had 100% uptime? ..not99.9999999% but /2 %!lots of little things (and a few big things) make thathappen. well done all!81#1NewKara Jones 1:54 PM100!!!!!! That is amazing!!! Power team!!Message #thank-yous+...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
18237
|
791
|
24
|
2026-05-11T11:06:06.543075+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-11/1778 /Users/lukas/.screenpipe/data/data/2026-05-11/1778497566543_m2.jpg...
|
PhpStorm
|
faVsco.js – laravel.log
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
JY-20725-handle-HS-search Project: faVsco.js, menu
JY-20725-handle-HS-search-rate-limit, menu
Start Listening for PHP Debug Connections
HandleHubspotRateLimitTest
Run 'HandleHubspotRateLimitTest'
Debug 'HandleHubspotRateLimitTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"bounds":{"left":0.025930852,"top":0.019952115,"width":0.03856383,"height":0.025538707},"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JY-20725-handle-HS-search-rate-limit, menu","depth":5,"bounds":{"left":0.064494684,"top":0.019952115,"width":0.09541223,"height":0.025538707},"on_screen":true,"help_text":"Git Branch: JY-20725-handle-HS-search-rate-limit","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"bounds":{"left":0.82413566,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"HandleHubspotRateLimitTest","depth":6,"bounds":{"left":0.8394282,"top":0.019952115,"width":0.076130316,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'HandleHubspotRateLimitTest'","depth":6,"bounds":{"left":0.9155585,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'HandleHubspotRateLimitTest'","depth":6,"bounds":{"left":0.9268617,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"bounds":{"left":0.9381649,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"bounds":{"left":0.96609044,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"bounds":{"left":0.9773936,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"bounds":{"left":0.9886968,"top":0.019952115,"width":0.011303186,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
1579713784452094674
|
-8780890023316608054
|
click
|
hybrid
|
NULL
|
Project: faVsco.js, menu
JY-20725-handle-HS-search Project: faVsco.js, menu
JY-20725-handle-HS-search-rate-limit, menu
Start Listening for PHP Debug Connections
HandleHubspotRateLimitTest
Run 'HandleHubspotRateLimitTest'
Debug 'HandleHubspotRateLimitTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
PhostormVIewINavicareCodeLaravelKeractorlooIsWindowFV faVsco.js?9 JY-20725-handle-HS-search-rate-limitProiectC) TrackAutomated ReportGeneratedzventonp(C) TranscodeParameterResc© UserService.php(c) Uuid.pnp> D TraitsD UseCases> D User> D Utils› D Validation> OvOphp nelpers.ong© InitialFrontendState.php© Jiminny.phpc) Plan.oho© Serializer.phpC) TeamScimDetails.ohpbootstrap>© build.> contia>D contrib→ database>O docsM front-end>D lang> node_modules library rootM ohostan> M nublic>O resourcesv Mroutesphp api.phpphp api_v2.phppnp console.onopnp customer_api.onppnp embedded.ongphp nealtn.onppnp scim.onophp uprotectedweb.phpphp web.phpphp webhook.php>O scriptsv O storage•aoo> M debuabar.… M frameworkv Mloas.aitianore• audio wav= custom.loalT SyncCrmEntitiesTrait.phpC) CachedCrmServiceDecorator.ongT DeleteCrmEntityTrait.php© RateLimitExce© PaginationConfia.phdclass JiminnyDebuqcommand extends Command361363364367368379=hubsnot-iournal-noll.loa= laravel log< nhnunit ymus tht is= oauth-nrivate kevTacts naccod. 12 (todav 12:02private function natelamitfor (Si = 0 ; $i < 3; $i++) {if ($i % 25 === 0) {scnis"pinto syneine opporcunity 11rSthis->info( string: "Matching contact {$i}"):scrmservice->syncupporcun1cy574/20504'scrmservice->macchbyname "kobot"1 usageprivate function simulateMatchActivityStorm(int SteamId = 2, int $count = 100): voidSconfio = steam->oetcrmconflourat.ion0Sactivities = Activitv:quervoerm confiauration id' Sconfio->aetidon->orderBvDescdi->Limit(Scount)->getO:Sthis->info( string: "Dispatching {Sactivities->count} MatchActivityCrmData jobs (portal={$config->getidO})"):fonpach (Sactivities as Cactivitv) 4MatchActivityCrmData::dispatch( ...arguments: Sactivity->getId, $config, true);Sthis->info( string: 'Done. Watch logs and run jiminny:debug observeRateLimit to inspect cache state.'):no usagesprivate function simulateVerifyTaskStorm(int SteamId = 2, int $count = 100): voidSactavitles = Activity::quervol>where co'team_1d', Steamid)s: 'crm provider id')'1d)->net05Sthis->info( string: "Dispatching (Sactivities->count} VerifyActivityCrmiaskJob jobs");foneach (Sactivities as Sactivitv) $(c) HubSpot/Service.onpMon 11 May 14:06:06console [STAGINGM A5 A133 X11 ^ v= custom.loglaravel.logA SF [jiminny@localhostQ- 429E= 100101=102= 103104105[ 106113=116[2026-05-11 11:05:37] local.INF0: [Prospect match] Resolved cor2026-05-11 11:05:371 1oc01TNE0: CrmActivitvServicell[2026-05-11 11:05:37] local.INF0: [CrmActivityService][2026-05-11 11:05:37] local.INFO: [ EsUpdateTarget[2026-05-11 11:05:37] Local.INFO: [ Asyn[2026-05-11 11:05:37] local.INF0: [MatchActivityCrmData][2026-05-11 11:05:37] local.INF0: [MatchActivityCrmData] Starting[2026-05-11 11:05:37] local.INF0: [ EsUpdateTarget ] Update single[2026-05-11 11:05:37] local.INF0: [ AsyncUpdateElasticSearch[2026-05-11 11:05:37] local.INF0: [MatchActivityCrmDatal Participants[2026-05-11 11:05:37] local.INF0: [SocialAcco[2026-05-11 11:05:37] local.INF0: [SocialAccountServicel2026-05-11 11:05:57 Local.LNFU: Lenc2026-05-11 11:05:57 LocaL.LNFU: 1Cr.12026-05-11 11:05:57 LocoL.INru: IPro.12026-05-11 11:05:57 Local.INru: IPro.12026-05-11 11:05:371 LocaL.INF0: Pro:12026-05-11 11:05:381 LocaL.INFO:12026-05-11 11:05:381 LocaL.INF0: Crm.12026-05-11 11:05:381 LocaL.INF0: Crm.12026-05-11 11:05:381 Loc0L TNF012026-05-11 11:05:381 Loc0L TNF0:12026-05-11 11:05:381 Loco1TN0: MatchActivitverm2026-05-11 11:05:381 Zoca1 TNF0: Mat12026-05-11 11:05:381 loco1TNF0: 1 EslndateTarae1[2026-05-11 11:05:39] Local.INF0: [ Asyncl12026-05-11 11:05:391 loco1TNE0: MatchActivitvermDatall[2026-05-11 11:05:39] local.INF0: [SocialAcco12026-05-11 11•05•301 1ocolTNS0•SocialAccountCervicell[2026-05-11 11:05:39] local.INF0: [EncryptedToken[2026-05-11 11:05:39] local.INF0: [Crm0wnerResolver] Integratior[2026-05-11 11:05:39] local.INF0: ProspectCache[2026-05-11 11:05:39] local.INF0: ProspectCache(2026-05-11 11:05:39] local.INF0: [Pro(2026-05-11 11:05:39] local.INF0: [CrmActivityServicel[2026-05-11 11:05:39] local.INF0: [CrmActivityServicel[2026-05-11 11:05:39] local.INF0: [ EsUpdateTarget ] Update single tarc[2026-05-11 11:05:39] local.INF0: [ AsyncUpdateElasticSearch ] Entity[2026-05-11 11:05:40] local.INF0: [MatchActivitvyCrmDatal Successfully matched CRM data12026-05-11 11:05:401 LocaL.INF0: MatchActiv1tvcrmbatal Startina CRM data[2026-05-11 11:05:41] local.INF0: [ EsUpdateTarget ] Update single target {"target"."a[2026-05-11 11:05:41] local.INF0: [ AsvncUpdateElasticSearch ] Entity adde12026-05-11 11:05:411 LocoLINF0: Mat12026-05-11 11:05:411 LocoL INFI: SoCh2026-05-11 11:05:411 LocaL,TNF0: Soci2026-05-11 11:05:411 LocaL,TNF0: 1Enc2026-05-11 11:05:411 Local.TNF0:1Cr2026-05-11 11:05:411 LocaL,TNF0: 1Pro.2026-05-11 11:05:411 LocaL TNF0:ICr12026-05-11 11•05•411 1ocol TNS0• ICom...
|
18234
|
NULL
|
NULL
|
NULL
|
|
18238
|
790
|
25
|
2026-05-11T11:06:11.551075+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-11/1778 /Users/lukas/.screenpipe/data/data/2026-05-11/1778497571551_m1.jpg...
|
PhpStorm
|
faVsco.js – laravel.log
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
SlackFileEditViewGoHistoryWindowHelp• 0DOCKERcompi SlackFileEditViewGoHistoryWindowHelp• 0DOCKERcompiledeventsroutesviews-₴81DEV (docker)$2APP (-zsh)883Jiminny-worker-processing-delayed: jiminny-worker-processing-delayed_00:stoppedjiminny-worker-processing-2:jiminny-worker-processing-2_00: stoppedjiminny-worker-processing-3:jiminny-worker-processing-3_00: stoppedjiminny-worker-processing-4:jiminny-worker-processing-4_00:stoppedjiminny-worker-processing-5: jiminny-worker-processing-5_00:stoppedworker-analytics:worker-analytics_00: stoppedworker-crm-update:worker-crm-update_00: stoppedworker-download:worker-download_00:stoppedworker-nudges:worker-nudges_00:stoppedjiminny-worker-processing-1:jiminny-worker-processing-1_00: stoppedworker:worker_00: stoppedworker-calendar:worker-calendar_00:worker-conferences:worker-conferences_00:worker-crm-sync:worker-crm-sync_00: stoppedworker-es-update:worker-es-update_00:worker-emails:worker-emails_00: stoppedworker-audio:worker-audio_00: stoppedartisan-schedule:artisan-schedule_00: stoppedartisan-schedule:artisan-schedule_00: startedjiminny-worker-processing-1:jiminny-worker-processing-1_00: startedjiminny-worker-processing-2:jiminny-worker-processing-2_00: startedjiminny-worker-processing-3:jiminny-worker-processing-3_00: startedjiminny-worker-processing-4:jiminny-worker-processing-4_00: startedjiminny-worker-processing-5:jiminny-worker-processing-5_00:startedjiminny-worker-processing-delayed: jiminny-worker-processing-delayed_00: startedworker:worker_00: startedworker-analytics:worker-analytics_00: startedworker-audio:worker-audio_00: startedworker-calendar:worker-calendar_00:startedworker-conferences:worker-conferences_00: startedworker-crm-sync:worker-crm-sync_00: startedworker-crm-update:worker-crm-update_00: startedworker-download:worker-download_00: startedworker-emails:worker-emails_00: startedworker-es-update:worker-es-update_00:startedworker-nudges:worker-nudges_00: startedroot@docker_lamp_1:/home/jiminny#php artisan jiminny:debugDispatching 100 MatchActivityCrmDatajobs (portal=2)Done.Watch logs and runjiminny:debug observeRateLimit to inspect cache state.root@docker_lamp_1:/home/jiminny# ]DEV (dl-zshHomeDMsActivityFilesLater...Morealo)• Preparation for Refi... in 54 m100% <78• Mon 11 May 14:06:11→Describe what you are looking forJiminny ...# contusion-clinic# curiosity_lab# engineering# general# jiminny-bg# platform-tickets# product_launches# random# releases# sofia-office# support# thank-yous# the_people_of jimi...^ Direct messagesP. Aneliya Angelova®. Galya Dimitrova& Petko Kashinski&. Stefka StoyanovaVasil Vasilev&. Nikolay IvanovAneliya Angelova, ...Stoyan Tanev• VesE Lukas Kovalik y…..::: AppsS Jira CloudToastGanala Cala# thank-yous406 0• Messagest Add canvas+O FilesWednesday, May 6th~It shows increaiuie skir tu jump into Supportand not miss a beat.More importantly, itshows great heart and helped delight ourcustomers on two ways at the same time:Support and Trials.You embodied so many of our values duringthis period.#selflessness #lowego #customerfocus#positiveattitudeView Recognition Moment• 1001 #101Đ1 reply 5 days agoToday ~Greg 1:31 PMShout out to engineering/infrastructure teamand their stability work. Did you know ... last weekour Product had 100% uptime? ..not99.9999999% but /2 %!lots of little things (and a few big things) make thathappen. well done all!#1NewKara Jones 1:54 PM100!!!!!! That is amazing!!! Power team!!Message #thank-yous...
|
NULL
|
5766287585137104424
|
NULL
|
click
|
ocr
|
NULL
|
SlackFileEditViewGoHistoryWindowHelp• 0DOCKERcompi SlackFileEditViewGoHistoryWindowHelp• 0DOCKERcompiledeventsroutesviews-₴81DEV (docker)$2APP (-zsh)883Jiminny-worker-processing-delayed: jiminny-worker-processing-delayed_00:stoppedjiminny-worker-processing-2:jiminny-worker-processing-2_00: stoppedjiminny-worker-processing-3:jiminny-worker-processing-3_00: stoppedjiminny-worker-processing-4:jiminny-worker-processing-4_00:stoppedjiminny-worker-processing-5: jiminny-worker-processing-5_00:stoppedworker-analytics:worker-analytics_00: stoppedworker-crm-update:worker-crm-update_00: stoppedworker-download:worker-download_00:stoppedworker-nudges:worker-nudges_00:stoppedjiminny-worker-processing-1:jiminny-worker-processing-1_00: stoppedworker:worker_00: stoppedworker-calendar:worker-calendar_00:worker-conferences:worker-conferences_00:worker-crm-sync:worker-crm-sync_00: stoppedworker-es-update:worker-es-update_00:worker-emails:worker-emails_00: stoppedworker-audio:worker-audio_00: stoppedartisan-schedule:artisan-schedule_00: stoppedartisan-schedule:artisan-schedule_00: startedjiminny-worker-processing-1:jiminny-worker-processing-1_00: startedjiminny-worker-processing-2:jiminny-worker-processing-2_00: startedjiminny-worker-processing-3:jiminny-worker-processing-3_00: startedjiminny-worker-processing-4:jiminny-worker-processing-4_00: startedjiminny-worker-processing-5:jiminny-worker-processing-5_00:startedjiminny-worker-processing-delayed: jiminny-worker-processing-delayed_00: startedworker:worker_00: startedworker-analytics:worker-analytics_00: startedworker-audio:worker-audio_00: startedworker-calendar:worker-calendar_00:startedworker-conferences:worker-conferences_00: startedworker-crm-sync:worker-crm-sync_00: startedworker-crm-update:worker-crm-update_00: startedworker-download:worker-download_00: startedworker-emails:worker-emails_00: startedworker-es-update:worker-es-update_00:startedworker-nudges:worker-nudges_00: startedroot@docker_lamp_1:/home/jiminny#php artisan jiminny:debugDispatching 100 MatchActivityCrmDatajobs (portal=2)Done.Watch logs and runjiminny:debug observeRateLimit to inspect cache state.root@docker_lamp_1:/home/jiminny# ]DEV (dl-zshHomeDMsActivityFilesLater...Morealo)• Preparation for Refi... in 54 m100% <78• Mon 11 May 14:06:11→Describe what you are looking forJiminny ...# contusion-clinic# curiosity_lab# engineering# general# jiminny-bg# platform-tickets# product_launches# random# releases# sofia-office# support# thank-yous# the_people_of jimi...^ Direct messagesP. Aneliya Angelova®. Galya Dimitrova& Petko Kashinski&. Stefka StoyanovaVasil Vasilev&. Nikolay IvanovAneliya Angelova, ...Stoyan Tanev• VesE Lukas Kovalik y…..::: AppsS Jira CloudToastGanala Cala# thank-yous406 0• Messagest Add canvas+O FilesWednesday, May 6th~It shows increaiuie skir tu jump into Supportand not miss a beat.More importantly, itshows great heart and helped delight ourcustomers on two ways at the same time:Support and Trials.You embodied so many of our values duringthis period.#selflessness #lowego #customerfocus#positiveattitudeView Recognition Moment• 1001 #101Đ1 reply 5 days agoToday ~Greg 1:31 PMShout out to engineering/infrastructure teamand their stability work. Did you know ... last weekour Product had 100% uptime? ..not99.9999999% but /2 %!lots of little things (and a few big things) make thathappen. well done all!#1NewKara Jones 1:54 PM100!!!!!! That is amazing!!! Power team!!Message #thank-yous...
|
18236
|
NULL
|
NULL
|
NULL
|
|
18239
|
790
|
26
|
2026-05-11T11:06:19.378468+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-11/1778 /Users/lukas/.screenpipe/data/data/2026-05-11/1778497579378_m1.jpg...
|
PhpStorm
|
faVsco.js – laravel.log
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
JY-20725-handle-HS-search Project: faVsco.js, menu
JY-20725-handle-HS-search-rate-limit, menu
Start Listening for PHP Debug Connections
HandleHubspotRateLimitTest...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JY-20725-handle-HS-search-rate-limit, menu","depth":5,"on_screen":true,"help_text":"Git Branch: JY-20725-handle-HS-search-rate-limit","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"HandleHubspotRateLimitTest","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
1684527879063584500
|
-8348266015821985344
|
click
|
hybrid
|
NULL
|
Project: faVsco.js, menu
JY-20725-handle-HS-search Project: faVsco.js, menu
JY-20725-handle-HS-search-rate-limit, menu
Start Listening for PHP Debug Connections
HandleHubspotRateLimitTest
SlackFileEditViewGoHistoryWindowHelp• 0DOCKERcompiledeventsroutesviews-₴81DEV (docker)$2APP (-zsh)883Jiminny-worker-processing-delayed: jiminny-worker-processing-delayed_00:stoppedjiminny-worker-processing-2:jiminny-worker-processing-2_00: stoppedjiminny-worker-processing-3:jiminny-worker-processing-3_00: stoppedjiminny-worker-processing-4:jiminny-worker-processing-4_00:stoppedjiminny-worker-processing-5: jiminny-worker-processing-5_00:stoppedworker-analytics:worker-analytics_00: stoppedworker-crm-update:worker-crm-update_00: stoppedworker-download:worker-download_00:stoppedworker-nudges:worker-nudges_00:stoppedjiminny-worker-processing-1:jiminny-worker-processing-1_00: stoppedworker:worker_00: stoppedworker-calendar:worker-calendar_00:worker-conferences:worker-conferences_00:worker-crm-sync:worker-crm-sync_00: stoppedworker-es-update:worker-es-update_00:worker-emails:worker-emails_00: stoppedworker-audio:worker-audio_00: stoppedartisan-schedule:artisan-schedule_00: stoppedartisan-schedule:artisan-schedule_00: startedjiminny-worker-processing-1:jiminny-worker-processing-1_00: startedjiminny-worker-processing-2:jiminny-worker-processing-2_00: startedjiminny-worker-processing-3:jiminny-worker-processing-3_00: startedjiminny-worker-processing-4:jiminny-worker-processing-4_00: startedjiminny-worker-processing-5:jiminny-worker-processing-5_00:startedjiminny-worker-processing-delayed: jiminny-worker-processing-delayed_00: startedworker:worker_00: startedworker-analytics:worker-analytics_00: startedworker-audio:worker-audio_00: startedworker-calendar:worker-calendar_00:startedworker-conferences:worker-conferences_00: startedworker-crm-sync:worker-crm-sync_00: startedworker-crm-update:worker-crm-update_00: startedworker-download:worker-download_00: startedworker-emails:worker-emails_00: startedworker-es-update:worker-es-update_00:startedworker-nudges:worker-nudges_00: startedroot@docker_lamp_1:/home/jiminny#php artisan jiminny:debugDispatching 100 MatchActivityCrmDatajobs (portal=2)Done.Watch logs and runjiminny:debug observeRateLimit to inspect cache state.root@docker_lamp_1:/home/jiminny# ]DEV (dl-zshHomeDMsActivityFilesLater...Morealo)• Preparation for Refi... in 54 m100% <78• Mon 11 May 14:06:19→Describe what you are looking forJiminny ...# contusion-clinic# curiosity_lab# engineering# general# jiminny-bg# platform-tickets# product_launches# random# releases# sofia-office# support# thank-yous# the_people_of jimi...^ Direct messagesP. Aneliya Angelova®. Galya Dimitrova& Petko Kashinski&. Stefka StoyanovaVasil Vasilev&. Nikolay IvanovAneliya Angelova, ...Stoyan Tanev• VesE Lukas Kovalik y…..::: AppsS Jira CloudToastGanala Cala# thank-yous406 0Messagest* Add canvas+O FilesWednesday, May 6th~It shows increaiuie skir tu jump into Supportand not miss a beat.More importantly, itshows great heart and helped delight ourcustomers on two ways at the same time:Support and Trials.You embodied so many of our values duringthis period.#selflessness #lowego #customerfocus#positiveattitudeView Recognition Moment011 reply 5 days agoToday ~Greg 1:31 PMShout out to engineering/infrastructure teamand their stability work. Did you know ... last weekour Product had 100% uptime? ..not99.9999999% but /2 %!lots of little things (and a few big things) make thathappen. well done all!81#1NewKara Jones 1:54 PM100!!!!!! That is amazing!!! Power team!!Message #thank-yous+...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
18240
|
791
|
25
|
2026-05-11T11:06:19.356608+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-11/1778 /Users/lukas/.screenpipe/data/data/2026-05-11/1778497579356_m2.jpg...
|
PhpStorm
|
faVsco.js – laravel.log
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
PhostormVIewINavicareCodeLaravelKeractorTOOISWindo PhostormVIewINavicareCodeLaravelKeractorTOOISWindowFV faVsco.js?9 JY-20725-handle-HS-search-rate-limitProiectC) TrackAutomated ReportGeneratedzventonp(C) TranscodeParameterRescl© UserService.php(c) Uuid.pnp> D TraitsD UseCases> D User> D Utils› D Validation> OvOphp nelpers.ong© InitialFrontendState.php© Jiminny.phpc) Plan.oho© Serializer.phpC) TeamScimDetails.ohpbootstrap>© build.> contia>D contrib→ database>O docsM front-end>D lang> node_modules library rootM ohostan> M nublic>O resourcesv Mroutesphp api.phpphp api_v2.phppnp console.onopnp customer_api.onppnp embedded.ongphp nealtn.onppnp scim.onophp uprotectedweb.phpphp web.phpphp webhook.php>O scriptsv O storage•aoo> M debuabar.… M frameworkv Mloas.aitianoree audio wav= custom.loalT SyncCrmEntitiesTrait.phpC) CachedCrmServiceDecorator.ong© RateLimitExce© PaginationConfia.phdclass JiminnyDebuqcommand extends Command361363364367368379=hubsnot-iournal-noll.log= laravel log< nhnunit ymus tht is= oauth-nrivate kevTacts naccod. 12 (todav 12:02private function natelamitfor (Si = 0 ; $i < 3; $i++) {if ($i % 25 === 0) {scnis"pinto syneine opporcunity 11rSthis->info( string: "Matching contact {Si}"):scrmservice->syncupporcun1cy 574/20564'scrmservice->macchbyname "kobot"1 usageprivate function simulateMatchActivityStorm(int Steamid = 2, int Scount = 100): voidSconfio = steam->oetcrmconflourat.ion0Sactivities = Activitv:quervoerm confiauration id' Sconfio->aetidon->orderBvDescdi->Limit(Scount)->getO:Sthis->info( string: "Dispatching {Sactivities->count} MatchActivityCrmData jobs (portal={Sconfig->getidOh)"):fonpach (Sactivities as Cactivitv) 4MatchActivityCrmData::dispatch( ...arguments: Sactivity->getId, $config, true):Sthis->info( string: 'Done. Watch logs and run jiminny:debug observeRateLimit to inspect cache state.'):no usagesprivate function simulateVerifyTaskStorm(int SteamId = 2, int $count = 100): voidSactavitles = Activity::quervol>where co'team_1d', Steamid)s: 'crm provider id')'1d)->net05Sthis->info( string: "Dispatching (Sactivities->count} VerifyActivityCrmTaskJob jobs");foneach (Sactivities as Cactivitv) !(c) HubSpot/Service.onpProviderkateLimiter.phgm| A5 A133 X11 ^ VNNNMon 11 May 14:06:19console [PRODA console [EUiconsole [STAGINGIL2026-05-11 11:06:08J local.INFO: LCrmActivityService) Email domain belongs to the team, skipping crm lookup l"activity_id":612562,"team_id":2,"email":"hoШẢẢẢẢẢ B[2026-05-11 11:06:08] local.INF0: [ EsUpdateTarget ] Update single[2026-05-11 11:06:08] local.INFO: [ AsyncUpdateElasticSearch ] Enti[2026-05-11 11:06:08] local.INF0: [MatchActivityCrmData][2026-05-11 11:06:09] local.INF0: [MatchActivityCrmData][2026-05-11 11:06:09]local.INFO: [ EsU;12020-00"11 14.00.071 Local.anrU. LASy.[2026-05-11 11:06:09]local.INF0: [MatchActivityCrmData][2026-05-11 11:06:09] local.INF0: [SocialAccountServicel Fetching token[2026-05-11 11:06:09]Local.Inru. socla[2026-05-11 11:06:09] local.INF0: [Encr›2026-05-11 11:06:041local.Inru:urmu[2026-05-11 11:06:09] local.INF0: [CrmA.2026-05-11 11:06:09Local.INFU:Pros[2026-05-11 11:06:101 local.INF0: [Hubsi12026-05-11 1:0611011LocaL.INFU:PrOSI[2026-05-11 11:06:101 local. INF0:2026-05-1111:06:1011CrmAt[2026-05-11 11:06:101 local.INF0: [ EsU:2026-05-1111:06:10/1LocaL.INF0: Asvncuodate lasticSearch[2026-05-11 11:06:10] Local.INFO:12026-05-11[2026-05-11 11:06:101 local, INF0: [ EsUiodateTargetLocalTNF0: AsvncUndate=lasticSearch[2026-05-1111:06:101 local.INFO:[MatchActivityCrmData]LocalTNF0:SocialAccountServicell12026-05-11 11:06-1171local.INFO:[SocialAccountService] Token retrieved 1'[2026-05-11 11:06:11] local.INF0: [EncryptedTokenManager] Generating access token[2026-05-11 11:06:11] local.INFO: [Crm0wnerResolver] Integration[2026-05-11 11:06:11] local.INF0:[Prospect match][2026-05-11 11:06:11]local.INFO: [Prospect match] cached[2026-05-11 11:06:11] local.INF0: [Prospect match] Resolved(2026-05-11 11:06:11] local.INF0: [CrmActivityServicel Email domain belong[2026-05-11 11:06:11] local.INF0: [CrmActivityServicel CRM matching complete[2026-05-11 11:06:11]local.INF0: [ EsUpdateTarget ] Update single target {"tar[2026-05-11 11:06:11] local.INF0: [ EsUpdateProcessManager[2026-05-11 11:06:11]Local.INF0: [ Asvi[2026-05-11 11:06:11] local.INF0: [MatchActivitvCrmDatal Successfully matched CRM data {"activity":61[2026-05-11 11:06:11] local.INF0: [MatchActivitvCrmDatal Starting CRM data matching {"activity":612559.[2026-05-11 11:06:11] local.INF0: [ EsU:4677825809210tyt ctaaina jiminnv com"? Sic12026-05-11 11:06:1111LocaL.INFU: ASVI[2026-05-11 11:06:11] local.INF0: [Matcl12026-05-11 11:06:111 LocaL.INF0: Soc1...
|
NULL
|
-8216006404531374782
|
NULL
|
click
|
ocr
|
NULL
|
PhostormVIewINavicareCodeLaravelKeractorTOOISWindo PhostormVIewINavicareCodeLaravelKeractorTOOISWindowFV faVsco.js?9 JY-20725-handle-HS-search-rate-limitProiectC) TrackAutomated ReportGeneratedzventonp(C) TranscodeParameterRescl© UserService.php(c) Uuid.pnp> D TraitsD UseCases> D User> D Utils› D Validation> OvOphp nelpers.ong© InitialFrontendState.php© Jiminny.phpc) Plan.oho© Serializer.phpC) TeamScimDetails.ohpbootstrap>© build.> contia>D contrib→ database>O docsM front-end>D lang> node_modules library rootM ohostan> M nublic>O resourcesv Mroutesphp api.phpphp api_v2.phppnp console.onopnp customer_api.onppnp embedded.ongphp nealtn.onppnp scim.onophp uprotectedweb.phpphp web.phpphp webhook.php>O scriptsv O storage•aoo> M debuabar.… M frameworkv Mloas.aitianoree audio wav= custom.loalT SyncCrmEntitiesTrait.phpC) CachedCrmServiceDecorator.ong© RateLimitExce© PaginationConfia.phdclass JiminnyDebuqcommand extends Command361363364367368379=hubsnot-iournal-noll.log= laravel log< nhnunit ymus tht is= oauth-nrivate kevTacts naccod. 12 (todav 12:02private function natelamitfor (Si = 0 ; $i < 3; $i++) {if ($i % 25 === 0) {scnis"pinto syneine opporcunity 11rSthis->info( string: "Matching contact {Si}"):scrmservice->syncupporcun1cy 574/20564'scrmservice->macchbyname "kobot"1 usageprivate function simulateMatchActivityStorm(int Steamid = 2, int Scount = 100): voidSconfio = steam->oetcrmconflourat.ion0Sactivities = Activitv:quervoerm confiauration id' Sconfio->aetidon->orderBvDescdi->Limit(Scount)->getO:Sthis->info( string: "Dispatching {Sactivities->count} MatchActivityCrmData jobs (portal={Sconfig->getidOh)"):fonpach (Sactivities as Cactivitv) 4MatchActivityCrmData::dispatch( ...arguments: Sactivity->getId, $config, true):Sthis->info( string: 'Done. Watch logs and run jiminny:debug observeRateLimit to inspect cache state.'):no usagesprivate function simulateVerifyTaskStorm(int SteamId = 2, int $count = 100): voidSactavitles = Activity::quervol>where co'team_1d', Steamid)s: 'crm provider id')'1d)->net05Sthis->info( string: "Dispatching (Sactivities->count} VerifyActivityCrmTaskJob jobs");foneach (Sactivities as Cactivitv) !(c) HubSpot/Service.onpProviderkateLimiter.phgm| A5 A133 X11 ^ VNNNMon 11 May 14:06:19console [PRODA console [EUiconsole [STAGINGIL2026-05-11 11:06:08J local.INFO: LCrmActivityService) Email domain belongs to the team, skipping crm lookup l"activity_id":612562,"team_id":2,"email":"hoШẢẢẢẢẢ B[2026-05-11 11:06:08] local.INF0: [ EsUpdateTarget ] Update single[2026-05-11 11:06:08] local.INFO: [ AsyncUpdateElasticSearch ] Enti[2026-05-11 11:06:08] local.INF0: [MatchActivityCrmData][2026-05-11 11:06:09] local.INF0: [MatchActivityCrmData][2026-05-11 11:06:09]local.INFO: [ EsU;12020-00"11 14.00.071 Local.anrU. LASy.[2026-05-11 11:06:09]local.INF0: [MatchActivityCrmData][2026-05-11 11:06:09] local.INF0: [SocialAccountServicel Fetching token[2026-05-11 11:06:09]Local.Inru. socla[2026-05-11 11:06:09] local.INF0: [Encr›2026-05-11 11:06:041local.Inru:urmu[2026-05-11 11:06:09] local.INF0: [CrmA.2026-05-11 11:06:09Local.INFU:Pros[2026-05-11 11:06:101 local.INF0: [Hubsi12026-05-11 1:0611011LocaL.INFU:PrOSI[2026-05-11 11:06:101 local. INF0:2026-05-1111:06:1011CrmAt[2026-05-11 11:06:101 local.INF0: [ EsU:2026-05-1111:06:10/1LocaL.INF0: Asvncuodate lasticSearch[2026-05-11 11:06:10] Local.INFO:12026-05-11[2026-05-11 11:06:101 local, INF0: [ EsUiodateTargetLocalTNF0: AsvncUndate=lasticSearch[2026-05-1111:06:101 local.INFO:[MatchActivityCrmData]LocalTNF0:SocialAccountServicell12026-05-11 11:06-1171local.INFO:[SocialAccountService] Token retrieved 1'[2026-05-11 11:06:11] local.INF0: [EncryptedTokenManager] Generating access token[2026-05-11 11:06:11] local.INFO: [Crm0wnerResolver] Integration[2026-05-11 11:06:11] local.INF0:[Prospect match][2026-05-11 11:06:11]local.INFO: [Prospect match] cached[2026-05-11 11:06:11] local.INF0: [Prospect match] Resolved(2026-05-11 11:06:11] local.INF0: [CrmActivityServicel Email domain belong[2026-05-11 11:06:11] local.INF0: [CrmActivityServicel CRM matching complete[2026-05-11 11:06:11]local.INF0: [ EsUpdateTarget ] Update single target {"tar[2026-05-11 11:06:11] local.INF0: [ EsUpdateProcessManager[2026-05-11 11:06:11]Local.INF0: [ Asvi[2026-05-11 11:06:11] local.INF0: [MatchActivitvCrmDatal Successfully matched CRM data {"activity":61[2026-05-11 11:06:11] local.INF0: [MatchActivitvCrmDatal Starting CRM data matching {"activity":612559.[2026-05-11 11:06:11] local.INF0: [ EsU:4677825809210tyt ctaaina jiminnv com"? Sic12026-05-11 11:06:1111LocaL.INFU: ASVI[2026-05-11 11:06:11] local.INF0: [Matcl12026-05-11 11:06:111 LocaL.INF0: Soc1...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
18241
|
791
|
26
|
2026-05-11T11:06:27.423344+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-11/1778 /Users/lukas/.screenpipe/data/data/2026-05-11/1778497587423_m2.jpg...
|
PhpStorm
|
faVsco.js – laravel.log
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
JY-20725-handle-HS-search Project: faVsco.js, menu
JY-20725-handle-HS-search-rate-limit, menu
Start Listening for PHP Debug Connections
HandleHubspotRateLimitTest
Run 'HandleHubspotRateLimitTest'
Debug 'HandleHubspotRateLimitTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
5
133
11
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Console\Commands;
use Carbon\Carbon;
use Carbon\CarbonImmutable;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Redis;
use InvalidArgumentException;
use Jiminny\Jobs\AutomatedReports\RequestGenerateAskJiminnyReportJob;
use Jiminny\Jobs\AutomatedReports\SendReportMailJob;
use Jiminny\Jobs\Crm\Delete\VerifyActivityCrmTaskJob;
use Jiminny\Jobs\Crm\MatchActivityCrmData;
use Jiminny\Jobs\JobDispatcherInterface;
use Jiminny\Models\Activity;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\AutomatedReportResult;
use Jiminny\Models\Team;
use Jiminny\Models\User;
use Jiminny\Repositories\AutomatedReportsRepository;
use Jiminny\Services\Activity\CrmOwnerResolver;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
/**
* Class JiminnyDebugCommand
*
* @package Jiminny\Console\Commands
*/
class JiminnyDebugCommand extends Command
{
public const string FREQUENCY_DAILY = 'daily';
public const string FREQUENCY_WEEKLY = 'weekly';
public const string FREQUENCY_MONTHLY = 'monthly';
public const string FREQUENCY_QUARTERLY = 'quarterly';
public const string FREQUENCY_ONE_OFF = 'one_off';
protected $signature = 'jiminny:debug';
public function handle(
JobDispatcherInterface $jobDispatcher,
AutomatedReportsService $automatedReportsService,
AutomatedReportsRepository $automatedReportsRepository,
UserPilotClient $userPilotClient
): void {
// Choose ONE of the following to run, then comment out the others.
// 1) Dispatch a storm of MatchActivityCrmData jobs against team 2
$this->simulateMatchActivityStorm(teamId: 2, count: 100);
// 2) Dispatch a storm of VerifyActivityCrmTaskJob jobs (simulates DeleteCrmEntityTrait fan-out)
// $this->simulateVerifyTaskStorm(teamId: 2, count: 100);
// 3) Inspect Redis circuit-breaker state for the team's HubSpot portal
// $this->observeRateLimitCache(teamId: 2);
// 4) Make 3 synchronous matchByName calls (foreground, hits API directly)
// $this->rateLimit();
exit(1);
$report = AutomatedReport::find(71);
$last = AutomatedReportResult::query()
->where('report_id', $report->getId())
->whereIn('status', [AutomatedReportResult::STATUS_DEFAULT, AutomatedReportResult::STATUS_FAILED])
// ->where('reason', '!=', AutomatedReportResult::REASON_NOT_ENOUGH_ACTIVITIES)
->whereDate('created_at', CarbonImmutable::now()->toDateString())
->latest()
->first();
$this->info("Last: {$last->getId()}");
exit(1);
$user = User::find(143);
// $count = $automatedReportsRepository->countUserReports($user);
// $this->info("Count: {$count}");
// $count = $automatedReportsRepository->countAllUserReports($user);
// $this->info("All count: {$count}");
$payload = [
'report_type' => 'ask_jiminny',
'frequency' => 'weekly',
];
$userPilotClient->track($user, 'ask-jiminny-report-generated', $payload);
exit(1);
$now = Carbon::now()->subDay(1);
$this->info("Now: {$now->toDateTimeString()}");
$weekStart = Carbon::getWeekStartsAt();
$this->info("Now: {$weekStart}");
// $from = $now->copy()->previousWeekday()->startOfDay();
// $to = $now->copy()->previousWeekday()->endOfDay();
// $fromOld = $now->copy()->subWeeks(1)->startOfDay();
// $toOld = $now->copy()->subDay()->endOfDay();
// $fromNew = $now->copy()->subWeek()->startOfWeek();
// $toNew = $now->copy()->subWeek()->endOfWeek();
// $fromOld = $now->copy()->subMonths(1)->startOfDay();
// $toOld = $now->copy()->subDay()->endOfDay();
// $fromNew = $now->copy()->subMonthNoOverflow()->startOfMonth();
// $toNew = $now->copy()->subMonthNoOverflow()->endOfMonth();
$fromOld = $now->copy()->subMonths(3)->startOfDay();
$toOld = $now->copy()->subDay()->endOfDay();
$fromNew = $now->copy()->subQuarterNoOverflow()->startOfQuarter();
$toNew = $now->copy()->subQuarterNoOverflow()->endOfQuarter();
$this->info("From old: {$fromOld->toDateTimeString()}");
$this->info("To old: {$toOld->toDateTimeString()}");
$this->info("From new: {$fromNew->toDateTimeString()}");
$this->info("To new: {$toNew->toDateTimeString()}");
exit(1);
$report = AutomatedReport::find(71);
$job = new RequestGenerateAskJiminnyReportJob($report->getUuid());
$jobDispatcher->dispatch($job);
exit(1);
// $this->formatDate($jobDispatcher);
// $this->sendMail($jobDispatcher, $automatedReportsService);
// $this->crmService();
$this->getPayload($automatedReportsService);
exit(1);
}
private function crmService()
{
$activity = Activity::find(418141);
$team = Team::find(19);
$config = $team->getCrmConfiguration();
$crmResolver = app(CrmOwnerResolver::class, [
'team' => $team,
'integrationAdmin' => $team->getOwner(),
'providerSlug' => $config->getProviderName(),
]);
$crmService = $crmResolver->prepareCrmService();
$crmService->createTranscriptNotes($activity);
}
private function sendMail(JobDispatcherInterface $jobDispatcher, AutomatedReportsService $automatedReportsService)
{
$reportUuid = '';
// $report = $automatedReportsService->getReportResult($reportUuid);
$report = AutomatedReportResult::find(275);
$validRecipients = $automatedReportsService->getValidRecipientUsers(
$report->getReport(),
includeJiminny: true,
);
$recipient = $validRecipients[0];
$fileName = $automatedReportsService->getReportFileName($report);
$typeName = $report->getReport()->getCustomName()
?? $automatedReportsService->getReportTypeName($report);
$teamsName = $automatedReportsService->getReportTeamsName($report);
$periodName = $automatedReportsService->getReportPeriodName($report);
$s3Path = $automatedReportsService->getMediaPath($report);
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$fileName ' . PHP_EOL . print_r($fileName, true));
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$typeName ' . PHP_EOL . print_r($typeName, true));
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$teamsName ' . PHP_EOL . print_r($teamsName, true));
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$periodName ' . PHP_EOL . print_r($periodName, true));
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$s3Path ' . PHP_EOL . print_r($s3Path, true));
$jobDispatcher->dispatch(
new SendReportMailJob(
reportUuid: $report->getUuid(),
s3Path: $s3Path,
recipientEmail: $recipient['email'],
recipientName: $recipient['name'] ?? null,
fileName: $fileName,
typeName: $typeName,
teamsName: $teamsName,
periodName: $periodName,
isAskJiminny: true,
)
);
exit(1);
}
private function formatDate(JobDispatcherInterface $jobDispatcher): void
{
$customName = 'Custom report name';
// $frequency = self::FREQUENCY_DAILY;
// $frequency = self::FREQUENCY_WEEKLY;
$frequency = self::FREQUENCY_MONTHLY;
// $frequency = self::FREQUENCY_QUARTERLY;
// $frequency = self::FREQUENCY_ONE_OFF;
$period = $this->calculateFromAndToDatePeriod($frequency);
$from = $period['fromDate'];
$to = $period['toDate'];
$periodName = $this->formatReportPeriodName($frequency, $from, $to);
$filenameSuffix = null;
if ($customName) {
if ($filenameSuffix) {
$customName .= " {$filenameSuffix}";
}
$result = $this->sanitizeFileName("{$customName} - {$periodName}");
}
$this->info($result);
}
public function calculateFromAndToDatePeriod(
string $frequency,
?Carbon $fromDate = null,
?Carbon $toDate = null
): array {
if ($frequency === self::FREQUENCY_ONE_OFF) {
return [
'fromDate' => $fromDate,
'toDate' => $toDate,
];
}
$now = Carbon::now();
return match ($frequency) {
self::FREQUENCY_DAILY => [
'fromDate' => $now->copy()->subDay()->startOfDay(),
'toDate' => $now->copy()->subDay()->endOfDay(),
],
self::FREQUENCY_WEEKLY => [
'fromDate' => $now->copy()->subWeeks(1)->startOfDay(),
'toDate' => $now->copy()->subDay()->endOfDay(),
],
self::FREQUENCY_MONTHLY => [
'fromDate' => $now->copy()->subMonths(1)->startOfDay(),
'toDate' => $now->copy()->subDay()->endOfDay(),
],
self::FREQUENCY_QUARTERLY => [
'fromDate' => $now->copy()->subMonths(3)->startOfDay(),
'toDate' => $now->copy()->subDay()->endOfDay(),
],
default => throw new InvalidArgumentException("Unsupported frequency: {$frequency}"),
};
}
private function formatReportPeriodName(string $frequency, Carbon $from, Carbon $to): string
{
$fromYear = $from->format('Y');
$toYear = $to->format('Y');
$differentYears = $fromYear !== $toYear;
switch ($frequency) {
case self::FREQUENCY_DAILY:
return $from->format('j M Y');
case self::FREQUENCY_QUARTERLY:
// 'Jan-Mar 2025' or 'Nov 2024-Jan 2025' if years differ
$startMonth = $from->format('M');
$endMonth = $to->copy()->subMonth();
$endMonthName = $endMonth->format('M');
$endMonthYear = $endMonth->format('Y');
if ($differentYears) {
return "{$startMonth} {$fromYear} - {$endMonthName} {$endMonthYear}";
}
return "{$startMonth} - {$endMonthName} {$toYear}";
case self::FREQUENCY_MONTHLY:
// 'May 2025' - monthly reports are always within the same year
return $from->format('M Y');
case self::FREQUENCY_WEEKLY:
// '4 - 8 Aug 2025', '27 Oct - 3 Nov 2025', or '28 Dec 2024 - 3 Jan 2025' if years differ
$startDay = $from->format('j');
$endDay = $to->format('j');
$startMonth = $from->format('M');
$endMonth = $to->format('M');
if ($differentYears) {
return "{$startDay} {$startMonth} {$fromYear} - {$endDay} {$endMonth} {$toYear}";
}
if ($startMonth !== $endMonth) {
return "{$startDay} {$startMonth} - {$endDay} {$endMonth} {$toYear}";
}
return "{$startDay} - {$endDay} {$endMonth} {$toYear}";
case self::FREQUENCY_ONE_OFF:
// '2 May-31 May 2025' or '15 Dec 2024-15 Jan 2025' if years differ
$startDay = $from->format('j');
$startMonth = $from->format('M');
$endDay = $to->format('j');
$endMonth = $to->format('M');
// If same month and year, use a format like '2-31 May 2025'
if ($startMonth === $endMonth && ! $differentYears) {
return "{$startDay} - {$endDay} {$startMonth} {$toYear}";
}
// If different years, include both years
if ($differentYears) {
return "{$startDay} {$startMonth} {$fromYear} - {$endDay} {$endMonth} {$toYear}";
}
// Same year but different months
return "{$startDay} {$startMonth} - {$endDay} {$endMonth} {$toYear}";
default:
// Default format for unknown frequencies
return $from->format('j M Y') . ' - ' . $to->format('j M Y');
}
}
public function sanitizeFileName(string $fileName): string
{
return str_replace(['/', '\\'], '-', $fileName);
}
private function getPayload(AutomatedReportsService $automatedReportsService)
{
$reportResult = AutomatedReportResult::find(269);
$automatedReport = $reportResult->getReport();
$activityIds = [1,2,3];
$payload = $automatedReportsService->getAskJiminnyGenerateReportPayload(
automatedReport: $automatedReport,
reportResult: $reportResult,
activityIds: $activityIds,
);
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$payload ' . PHP_EOL . print_r($payload, true));
}
private function rateLimit()
{
$team = Team::find(2);
$config = $team->getCrmConfiguration();
$crmResolver = app(CrmOwnerResolver::class, [
'team' => $team,
'integrationAdmin' => $team->getOwner(),
'providerSlug' => $config->getProviderName(),
]);
$crmService = $crmResolver->prepareCrmService();
for ($i = 0 ; $i < 3; $i++) {
// if ($i % 25 === 0) {
// $this->info("Syncing opportunity {$i}");
$this->info("Matching contact {$i}");
// }
// $crmService->syncOpportunity('374720564');
$crmService->matchByName('Robot');
}
}
private function simulateMatchActivityStorm(int $teamId = 2, int $count = 100): void
{
$team = Team::find($teamId);
$config = $team->getCrmConfiguration();
$activities = Activity::query()
->where('crm_configuration_id', $config->getId())
->orderByDesc('id')
->limit($count)
->get();
$this->info("Dispatching {$activities->count()} MatchActivityCrmData jobs (portal={$config->getId()})");
foreach ($activities as $activity) {
MatchActivityCrmData::dispatch($activity->getId(), $config, true);
}
$this->info('Done. Watch logs and run jiminny:debug observeRateLimit to inspect cache state.');
}
private function simulateVerifyTaskStorm(int $teamId = 2, int $count = 100): void
{
$activities = Activity::query()
->where('team_id', $teamId)
->whereNotNull('crm_provider_id')
->orderByDesc('id')
->limit($count)
->get();
$this->info("Dispatching {$activities->count()} VerifyActivityCrmTaskJob jobs");
foreach ($activities as $activity) {
VerifyActivityCrmTaskJob::dispatch($activity->getId());
}
$this->info('Done.');
}
private function observeRateLimitCache(int $teamId = 2): void
{
$team = Team::find($teamId);
$config = $team->getCrmConfiguration();
$key = sprintf('hubspot:ratelimit:portal:%d', $config->getId());
$value = Redis::get($key);
$ttl = Redis::ttl($key);
$this->info("Redis key: {$key}");
$this->info('Value: ' . ($value ?? '(empty)'));
$this->info("TTL: {$ttl}s");
}
}
Show Replace Field
Search History
429
New Line
Match Case
Words
Regex
Replace History
Replace
New Line
Preserve case
0 results
Previous Occurrence
Next Occurrence
Filter Search Results
Open in Window, Multiple Cursors
Click to highlight
Close
Sync Changes
Hide This Notification
Code changed:
Hide
124
Previous Highlighted Error
Next Highlighted Error
[2026-05-11 11:06:08] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {"activity_id":612562,"team_id":2,"email":"[EMAIL]"} {"correlation_id":"c1949c5e-c0f4-431e-ba26-59d49408d871","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:08] local.INFO: [CrmActivityService] CRM matching completed {"activity_id":612562,"participants_processed":2,"exact_matches":0,"domain_matches":1,"best_match_found":true} {"correlation_id":"c1949c5e-c0f4-431e-ba26-59d49408d871","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:08] local.INFO: [ EsUpdateTarget ] Update single target {"target":"activities","purpose":"searchable-observer-update","entityId":612562} {"correlation_id":"c1949c5e-c0f4-431e-ba26-59d49408d871","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:08] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {"entityType":"activities","entityId":612562,"collectionKey":"activities-for-update-priority","withPriority":true} {"correlation_id":"c1949c5e-c0f4-431e-ba26-59d49408d871","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:08] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {"activity":612562,"remote_search":true,"lead_id":null,"contact_id":null,"account_id":26,"opportunity_id":22,"stage_id":89} {"correlation_id":"c1949c5e-c0f4-431e-ba26-59d49408d871","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:09] local.INFO: [MatchActivityCrmData] Starting CRM data matching {"activity":612561,"remote_search":true,"set_configuration":2,"old_state":{"lead_id":null,"contact_id":null,"account_id":26,"opportunity_id":22,"stage_id":89}} {"correlation_id":"b715f531-04da-410f-be31-055aa1c13754","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:09] local.INFO: [ EsUpdateTarget ] Update single target {"target":"activities","purpose":"searchable-observer-update","entityId":612561} {"correlation_id":"b715f531-04da-410f-be31-055aa1c13754","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:09] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {"entityType":"activities","entityId":612561,"collectionKey":"activities-for-update-priority","withPriority":true} {"correlation_id":"b715f531-04da-410f-be31-055aa1c13754","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:09] local.INFO: [MatchActivityCrmData] Participants old state {"activity":612561,"participants":[{"id":999780,"user_id":206,"contact_id":null,"lead_id":null},{"id":999781,"user_id":null,"contact_id":null,"lead_id":null}]} {"correlation_id":"b715f531-04da-410f-be31-055aa1c13754","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:09] local.INFO: [SocialAccountService] Fetching token {"socialAccountId":1499,"provider":"hubspot"} {"correlation_id":"b715f531-04da-410f-be31-055aa1c13754","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:09] local.INFO: [SocialAccountService] Token retrieved {"socialAccountId":1499,"provider":"hubspot"} {"correlation_id":"b715f531-04da-410f-be31-055aa1c13754","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:09] local.INFO: [EncryptedTokenManager] Generating access token. {"mode":"legacy"} {"correlation_id":"b715f531-04da-410f-be31-055aa1c13754","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:09] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {"crm_provider":"hubspot","crm_owner":148,"team_id":2} {"correlation_id":"b715f531-04da-410f-be31-055aa1c13754","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:09] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {"activity_id":612561,"team_id":2,"email":"[EMAIL]"} {"correlation_id":"b715f531-04da-410f-be31-055aa1c13754","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:09] local.INFO: [Prospect match] Cache miss, calling the API {"identifier_type":"email","identifier":"[EMAIL]"} {"correlation_id":"b715f531-04da-410f-be31-055aa1c13754","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:10] local.INFO: [Hubspot] Failed to fetch contact {"email":"[EMAIL]","reason":"[404] Client error: `GET https://api.hubapi.com/crm/v3/objects/contacts/447700174614.447782589921.OeREojLVnk%40txt.staging.jiminny.com?properties=email%2Cfirstname%2Clastname%2Ccountry%2Cphone%2Cmobilephone%2Cjobtitle%2Chubspot_owner_id%2Cassociatedcompanyid%2Cphoto&archived=0&idProperty=email` resulted in a `404 Not Found` response"} {"correlation_id":"b715f531-04da-410f-be31-055aa1c13754","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:10] local.INFO: [Prospect match] API returned empty result, caching the miss with empty prospect data {"identifier_type":"email","identifier":"[EMAIL]"} {"correlation_id":"b715f531-04da-410f-be31-055aa1c13754","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:10] local.INFO: [Prospect match] Resolved company domain from email {"email":"[EMAIL]","domain":"jiminny.com"} {"correlation_id":"b715f531-04da-410f-be31-055aa1c13754","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:10] local.INFO: [CrmActivityService] CRM matching completed {"activity_id":612561,"participants_processed":2,"exact_matches":0,"domain_matches":1,"best_match_found":true} {"correlation_id":"b715f531-04da-410f-be31-055aa1c13754","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:10] local.INFO: [ EsUpdateTarget ] Update single target {"target":"activities","purpose":"searchable-observer-update","entityId":612561} {"correlation_id":"b715f531-04da-410f-be31-055aa1c13754","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:10] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {"entityType":"activities","entityId":612561,"collectionKey":"activities-for-update-priority","withPriority":true} {"correlation_id":"b715f531-04da-410f-be31-055aa1c13754","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:10] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {"activity":612561,"remote_search":true,"lead_id":null,"contact_id":null,"account_id":26,"opportunity_id":22,"stage_id":89} {"correlation_id":"b715f531-04da-410f-be31-055aa1c13754","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:10] local.INFO: [MatchActivityCrmData] Starting CRM data matching {"activity":612560,"remote_search":true,"set_configuration":2,"old_state":{"lead_id":null,"contact_id":null,"account_id":26,"opportunity_id":22,"stage_id":89}} {"correlation_id":"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:10] local.INFO: [ EsUpdateTarget ] Update single target {"target":"activities","purpose":"searchable-observer-update","entityId":612560} {"correlation_id":"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:10] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {"entityType":"activities","entityId":612560,"collectionKey":"activities-for-update-priority","withPriority":true} {"correlation_id":"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:10] local.INFO: [MatchActivityCrmData] Participants old state {"activity":612560,"participants":[{"id":999778,"user_id":null,"contact_id":null,"lead_id":null},{"id":999779,"user_id":206,"contact_id":null,"lead_id":null}]} {"correlation_id":"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [SocialAccountService] Fetching token {"socialAccountId":1499,"provider":"hubspot"} {"correlation_id":"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [SocialAccountService] Token retrieved {"socialAccountId":1499,"provider":"hubspot"} {"correlation_id":"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [EncryptedTokenManager] Generating access token. {"mode":"legacy"} {"correlation_id":"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {"crm_provider":"hubspot","crm_owner":148,"team_id":2} {"correlation_id":"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [Prospect match] Cache / local search hit {"identifier_type":"email","identifier":"[EMAIL]"} {"correlation_id":"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [Prospect match] cached empty result - no API calls, try next matching method {"identifier_type":"email","identifier":"[EMAIL]"} {"correlation_id":"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [Prospect match] Resolved company domain from email {"email":"[EMAIL]","domain":"jiminny.com"} {"correlation_id":"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {"activity_id":612560,"team_id":2,"email":"[EMAIL]"} {"correlation_id":"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [CrmActivityService] CRM matching completed {"activity_id":612560,"participants_processed":2,"exact_matches":0,"domain_matches":1,"best_match_found":true} {"correlation_id":"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [ EsUpdateTarget ] Update single target {"target":"activities","purpose":"searchable-observer-update","entityId":612560} {"correlation_id":"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [ EsUpdateProcessManager ] Finished updating entities in ES {"worker":"","peak_memory":"99.73 MB","elapsed_seconds":0.74,"update_target":"activities","should_iterate_again":false} {"correlation_id":"38935ae1-fcb3-437e-9c9e-1b7415a0b7e1","trace_id":"325a2f9b-9c66-4223-ac02-1a43723f4315"}
[2026-05-11 11:06:11] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {"entityType":"activities","entityId":612560,"collectionKey":"activities-for-update-priority","withPriority":true} {"correlation_id":"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {"activity":612560,"remote_search":true,"lead_id":null,"contact_id":null,"account_id":26,"opportunity_id":22,"stage_id":89} {"correlation_id":"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [MatchActivityCrmData] Starting CRM data matching {"activity":612559,"remote_search":true,"set_configuration":2,"old_state":{"lead_id":null,"contact_id":97,"account_id":69,"opportunity_id":5011,"stage_id":34}} {"correlation_id":"4d59a59c-38a5-4a1d-9379-6ceaf67b747e","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [ EsUpdateTarget ] Update single target {"target":"activities","purpose":"searchable-observer-update","entityId":612559} {"correlation_id":"4d59a59c-38a5-4a1d-9379-6ceaf67b747e","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {"entityType":"activities","entityId":612559,"collectionKey":"activities-for-update-priority","withPriority":true} {"correlation_id":"4d59a59c-38a5-4a1d-9379-6ceaf67b747e","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [MatchActivityCrmData] Participants old state {"activity":612559,"participants":[{"id":999776,"user_id":206,"contact_id":null,"lead_id":null},{"id":999777,"user_id":null,"contact_id":97,"lead_id":null}]} {"correlation_id":"4d59a59c-38a5-4a1d-9379-6ceaf67b747e","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [SocialAccountService] Fetching token {"socialAccountId":1499,"provider":"hubspot"} {"correlation_id":"4d59a59c-38a5-4a1d-9379-6ceaf67b747e","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [SocialAccountService] Token retrieved {"socialAccountId":1499,"provider":"hubspot"} {"correlation_id":"4d59a59c-38a5-4a1d-9379-6ceaf67b747e","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [EncryptedTokenManager] Generating access token. {"mode":"legacy"} {"correlation_id":"4d59a59c-38a5-4a1d-9379-6ceaf67b747e","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {"crm_provider":"hubspot","crm_owner":148,"team_id":2} {"correlation_id":"4d59a59c-38a5-4a1d-9379-6ceaf67b747e","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {"activity_id":612559,"team_id":2,"email":"[EMAIL]"} {"correlation_id":"4d59a59c-38a5-4a1d-9379-6ceaf67b747e","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: ProspectCache - Searching DB for opportunity by owner {"account_id":69,"contact_id":97,"owner_id":206} {"correlation_id":"4d59a59c-38a5-4a1d-9379-6ceaf67b747e","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: ProspectCache - Fallback DB opportunity search {"account_id":69,"contact_id":97} {"correlation_id":"4d59a59c-38a5-4a1d-9379-6ceaf67b747e","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: ProspectCache - Opportunity DB search results {"account_id":69,"contact_id":97,"opportunity_id":5011} {"correlation_id":"4d59a59c-38a5-4a1d-9379-6ceaf67b747e","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [Prospect match] Cache / local search hit {"identifier_type":"email","identifier":"[EMAIL]"} {"correlation_id":"4d59a59c-38a5-4a1d-9379-6ceaf67b747e","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [CrmActivityService] CRM matching completed {"activity_id":612559,"participants_processed":2,"exact_matches":1,"domain_matches":0,"best_match_found":true} {"correlation_id":"4d59a59c-38a5-4a1d-9379-6ceaf67b747e","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [ EsUpdateTarget ] Update single target {"target":"activities","purpose":"searchable-observer-update","entityId":612559} {"correlation_id":"4d59a59c-38a5-4a1d-9379-6ceaf67b747e","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {"entityType":"activities","entityId":612559,"collectionKey":"activities-for-update-priority","withPriority":true} {"correlation_id":"4d59a59c-38a5-4a1d-9379-6ceaf67b747e","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {"activity":612559,"remote_search":true,"lead_id":null,"contact_id":97,"account_id":69,"opportunity_id":5011,"stage_id":34} {"correlation_id":"4d59a59c-38a5-4a1d-9379-6ceaf67b747e","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:12] local.INFO: [MatchActivityCrmData] Starting CRM data matching {"activity":612558,"remote_search":true,"set_configuration":2,"old_state":{"lead_id":null,"contact_id":97,"account_id":69,"opportunity_id":5011,"stage_id":34}} {"correlation_id":"a105800d-d2f7-4772-a534-7791e262461c","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:12] local.INFO: [ EsUpdateTarget ] Update single target {"target":"activities","purpose":"searchable-observer-update","entityId":612558} {"correlation_id":"a105800d-d2f7-4772-a534-7791e262461c","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:12] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {"entityType":"activities","entityId":612558,"collectionKey":"activities-for-update-priority","withPriority":true} {"correlation_id":"a105800d-d2f7-4772-a534-7791e262461c","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:12] local.INFO: [MatchActivityCrmData] Participants old state {"activity":612558,"participants":[{"id":999774,"user_id":206,"contact_id":null,"lead_id":null},{"id":999775,"user_id":null,"contact_id":97,"lead_id":null}]} {"correlation_id":"a105800d-d2f7-4772-a534-7791e262461c","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:12] local.INFO: [SocialAccountService] Fetching token {"socialAccountId":1499,"provider":"hubspot"} {"correlation_id":"a105800d-d2f7-4772-a534-7791e262461c","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:12] local.INFO: [SocialAccountService] Token retrieved {"socialAccountId":1499,"provider":"hubspot"} {"correlation_id":"a105800d-d2f7-4772-a534-7791e262461c","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:12] local.INFO: [EncryptedTokenManager] Generating access token. {"mode":"legacy"} {"correlation_id":"a105800d-d2f7-4772-a534-7791e262461c","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:12] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {"crm_provider":"hubspot","crm_owner":148,"team_id":2} {"correlation_id":"a105800d-d2f7-4772-a534-7791e262461c","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:12] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {"activity_id":612558,"team_id":2,"email":"[EMAIL]"} {"correlation_id":"a105800d-d2f7-4772-a534-7791e262461c","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:12] local.INFO: [Prospect match] Cache / local search hit {"identifier_type":"email","identifier":"[EMAIL]"} {"correlation_id":"a105800d-d2f7-4772-a534-7791e262461c","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:12] local.INFO: [CrmActivityService] CRM matching completed {"activity_id":612558,"participants_processed":2,"exact_matches":1,"domain_matches":0,"best_match_found":true} {"correlation_id":"a105800d-d2f7-4772-a534-7791e262461c","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:12] local.INFO: [ EsUpdateTarget ] Update single target {"target":"activities","purpose":"searchable-observer-update","entityId":612558} {"correlation_id":"a105800d-d2f7-4772-a534-7791e262461c","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:12] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {"entityType":"activities","entityId":612558,"collectionKey":"activities-for-update-priority","withPriority":true} {"correlation_id":"a105800d-d2f7-4772-a534-7791e262461c","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:12] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {"activity":612558,"remote_search":true,"lead_id":null,"contact_id":97,"account_id":69,"opportunity_id":5011,"stage_id":34} {"correlation_id":"a105800d-d2f7-4772-a534-7791e262461c","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:13] local.INFO: [MatchActivityCrmData] Starting CRM data matching {"activity":612557,"remote_search":true,"set_configuration":2,"old_state":{"lead_id":null,"contact_id":97,"account_id":69,"opportunity_id":5011,"stage_id":34}} {"correlation_id":"3ec57437-5796-4e44-8bf0-aaec149ac813","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:13] local.INFO: [ EsUpdateTarget ] Update single target {"target":"activities","purpose":"searchable-observer-update","entityId":612557} {"correlation_id":"3ec57437-5796-4e44-8bf0-aaec149ac813","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:13] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {"entityType":"activities","entityId":612557,"collectionKey":"activities-for-update-priority","withPriority":true} {"correlation_id":"3ec57437-5796-4e44-8bf0-aaec149ac813","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:13] local.INFO: [MatchActivityCrmData] Participants old state {"activity":612557,"participants":[{"id":999772,"user_id":206,"contact_id":null,"lead_id":null},{"id":999773,"user_id":null,"contact_id":97,"lead_id":null}]} {"correlation_id":"3ec57437-5796-4e44-8bf0-aaec149ac813","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:13] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"mailbox:skip-lists:refresh","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.727} {"correlation_id":"5203040f-23be-43e6-8410-ba49ee1b38b4","trace_id":"e787953d-9b78-4b63-a987-4f93f6892984"}
[2026-05-11 11:06:13] local.INFO: [SocialAccountService] Fetching token {"socialAccountId":1499,"provider":"hubspot"} {"correlation_id":"3ec57437-5796-4e44-8bf0-aaec149ac813","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:13] local.INFO: [SocialAccountService] Token retrieved {"socialAccountId":1499,"provider":"hubspot"} {"correlation_id":"3ec57437-5796-4e44-8bf0-aaec149ac813","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:13] local.INFO: [EncryptedTokenManager] Generating access token. {"mode":"legacy"} {"correlation_id":"3ec57437-5796-4e44-8bf0-aaec149ac813","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:13] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {"crm_provider":"hubspot","crm_owner":148,"team_id":2} {"correlation_id":"3ec57437-5796-4e44-8bf0-aaec149ac813","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:13] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {"activity_id":612557,"team_id":2,"email":"[EMAIL]"} {"correlation_id":"3ec57437-5796-4e44-8bf0-aaec149ac813","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:13] local.INFO: [Prospect match] Cache / local search hit {"identifier_type":"email","identifier":"[EMAIL]"} {"correlation_id":"3ec57437-5796-4e44-8bf0-aaec149ac813","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:13] local.INFO: [CrmActivityService] CRM matching completed {"activity_id":612557,"participants_processed":2,"exact_matches":1,"domain_matches":0,"best_match_found":true} {"correlation_id":"3ec57437-5796-4e44-8bf0-aaec149ac813","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:13] local.INFO: [ EsUpdateTarget ] Update single target {"target":"activities","purpose":"searchable-observer-update","entityId":612557} {"correlation_id":"3ec57437-5796-4e44-8bf0-aaec149ac813","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:13] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {"entityType":"activities","entityId":612557,"collectionKey":"activities-for-update-priority","withPriority":true} {"correlation_id":"3ec57437-5796-4e44-8bf0-aaec149ac813","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:13] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {"activity":612557,"remote_search":true,"lead_id":null,"contact_id":97,"account_id":69,"opportunity_id":5011,"stage_id":34} {"correlation_id":"3ec57437-5796-4e44-8bf0-aaec149ac813","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:13] local.INFO: Jiminny\Console\Commands\Command::run Memory usage for command {"command":"mailbox:skip-lists:refresh","memoryBeforeCommandInMb":60.0,"memoryAfterCommandInMB":60.0,"memoryPeakBeforeCommandInMb":99.727,"memoryPeakAfterCommandInMB":99.727} {"correlation_id":"5203040f-23be-43e6-8410-ba49ee1b38b4","trace_id":"e787953d-9b78-4b63-a987-4f93f6892984"}
[2026-05-11 11:06:13] local.INFO: [MatchActivityCrmData] Starting CRM data matching {"activity":612556,"remote_search":true,"set_configuration":2,"old_state":{"lead_id":null,"contact_id":97,"account_id":69,"opportunity_id":5011,"stage_id":34}} {"correlation_id":"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:13] local.INFO: [ EsUpdateTarget ] Update single target {"target":"activities","purpose":"searchable-observer-update","entityId":612556} {"correlation_id":"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:13] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {"entityType":"activities","entityId":612556,"collectionKey":"activities-for-update-priority","withPriority":true} {"correlation_id":"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:13] local.INFO: [MatchActivityCrmData] Participants old state {"activity":612556,"participants":[{"id":999770,"user_id":206,"contact_id":null,"lead_id":null},{"id":999771,"user_id":null,"contact_id":97,"lead_id":null}]} {"correlation_id":"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:13] local.INFO: [SocialAccountService] Fetching token {"socialAccountId":1499,"provider":"hubspot"} {"correlation_id":"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:13] local.INFO: [SocialAccountService] Token retrieved {"socialAccountId":1499,"provider":"hubspot"} {"correlation_id":"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:13] local.INFO: [EncryptedTokenManager] Generating access token. {"mode":"legacy"} {"correlation_id":"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:13] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {"crm_provider":"hubspot","crm_owner":148,"team_id":2} {"correlation_id":"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:14] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {"activity_id":612556,"team_id":2,"email":"[EMAIL]"} {"correlation_id":"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:14] local.INFO: [Prospect match] Cache / local search hit {"identifier_type":"email","identifier":"[EMAIL]"} {"correlation_id":"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:14] local.INFO: [CrmActivityService] CRM matching completed {"activity_id":612556,"participants_processed":2,"exact_matches":1,"domain_matches":0,"best_match_found":true} {"correlation_id":"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:14] local.INFO: [ EsUpdateTarget ] Update single target {"target":"activities","purpose":"searchable-observer-update","entityId":612556} {"correlation_id":"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:14] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {"entityType":"activities","entityId":612556,"collectionKey":"activities-for-update-priority","withPriority":true} {"correlation_id":"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:14] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {"activity":612556,"remote_search":true,"lead_id":null,"contact_id":97,"account_id":69,"opportunity_id":5011,"stage_id":34} {"correlation_id":"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:14] local.INFO: [MatchActivityCrmData] Starting CRM data matching {"activity":612555,"remote_search":true,"set_configuration":2,"old_state":{"lead_id":null,"contact_id":97,"account_id":69,"opportunity_id":5011,"stage_id":34}} {"correlation_id":"3e9d1a70-5491-44ae-8f28-5cb9b60e1859","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:14] local.INFO: [ EsUpdateTarget ] Update single target {"target":"activities","purpose":"searchable-observer-update","entityId":612555} {"correlation_id":"3e9d1a70-5491-44ae-8f28-5cb9b60e1859","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:14] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {"entityType":"activities","entityId":612555,"collectionKey":"activities-for-update-priority","withPriority":true} {"correlation_id":"3e9d1a70-5491-44ae-8f28-5cb9b60e1859","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:14] local.INFO: [MatchActivityCrmData] Participants old state {"activity":612555,"participants":[{"id":999768,"user_id":206,"contact_id":null,"lead_id":null},{"id":999769,"user_id":null,"contact_id":97,"lead_id":null}]} {"correlation_id":"3e9d1a70-5491-44ae-8f28-5cb9b60e1859","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:14] local.INFO: [SocialAccountService] Fetching token {"socialAccountId":1499,"provider":"hubspot"} {"correlation_id":"3e9d1a70-5491-44ae-8f28-5cb9b60e1859","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:14] local.INFO: [SocialAccountService] Token retrieved {"socialAccountId":1499,"provider":"hubspot"} {"correlation_id":"3e9d1a70-5491-44ae-8f28-5cb9b60e1859","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:14] local.INFO: [EncryptedTokenManager] Generating access token. {"mode":"legacy"} {"correlation_id":"3e9d1a70-5491-44ae-8f28-5cb9b60e1859","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:14] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {"crm_provider":"hubspot","crm_owner":148,"team_id":2} {"correlation_id":"3e9d1a70-5491-44ae-8f28-5cb9b60e1859","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:14] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {"activity_id":612555,"team_id":2,"email":"[EMAIL]"} {"correlation_id":"3e9d1a70-5491-44ae-8f28-5cb9b60e1859","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:14] local.INFO: [Prospect match] Cache / local search hit {"identifier_type":"email","identifier":"[EMAIL]"} {"correlation_id":"3e9d1a70-5491-44ae-8f28-5cb9b60e1859","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:14] local.INFO: [CrmActivityService] CRM matching completed {"activity_id":612555,"participants_processed":2,"exact_matches":1,"domain_matches":0,"best_match_found":true} {"correlation_id":"3e9d1a70-5491-44ae-8f28-5cb9b60e1859","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:14] local.INFO: [ EsUpdateTarget ] Update single target {"target":"activities","purpose":"searchable-observer-update","entityId":612555} {"correlation_id":"3e9d1a70-5491-44ae-8f28-5cb9b60e1859","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:14] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {"entityType":"activities","entityId":612555,"collectionKey":"activities-for-update-priority","withPriority":true} {"correlation_id":"3e9d1a70-5491-44ae-8f28-5cb9b60e1859","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:14] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {"activity":612555,"remote_search":true,"lead_id":null,"contact_id":97,"account_id":69,"opportunity_id":5011,"stage_id":34} {"correlation_id":"3e9d1a70-5491-44ae-8f28-5cb9b60e1859","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:14] local.INFO: [MatchActivityCrmData] Starting CRM data matching {"activity":612554,"remote_search":true,"set_configuration":2,"old_state":{"lead_id":null,"contact_id":97,"account_id":69,"opportunity_id":5011,"stage_id":34}} {"correlation_id":"7101e979-3a7e-4b2c-bf04-dc6a24ba1356","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:14] local.INFO: [ EsUpdateTarget ] Update single target {"target":"activities","purpose":"searchable-observer-update","entityId":612554} {"correlation_id":"7101e979-3a7e-4b2c-bf04-dc6a24ba1356","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:14] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {"entityType":"activities","entityId":612554,"collectionKey":"activities-for-update-prio...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"bounds":{"left":0.025930852,"top":0.019952115,"width":0.03856383,"height":0.025538707},"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JY-20725-handle-HS-search-rate-limit, menu","depth":5,"bounds":{"left":0.064494684,"top":0.019952115,"width":0.09541223,"height":0.025538707},"on_screen":true,"help_text":"Git Branch: JY-20725-handle-HS-search-rate-limit","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"bounds":{"left":0.82413566,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"HandleHubspotRateLimitTest","depth":6,"bounds":{"left":0.8394282,"top":0.019952115,"width":0.076130316,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'HandleHubspotRateLimitTest'","depth":6,"bounds":{"left":0.9155585,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'HandleHubspotRateLimitTest'","depth":6,"bounds":{"left":0.9268617,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"bounds":{"left":0.9381649,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"bounds":{"left":0.96609044,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"bounds":{"left":0.9773936,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"bounds":{"left":0.9886968,"top":0.019952115,"width":0.011303186,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"5","depth":4,"bounds":{"left":0.5043218,"top":0.17478053,"width":0.007978723,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"133","depth":4,"bounds":{"left":0.5142952,"top":0.17478053,"width":0.011968086,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"11","depth":4,"bounds":{"left":0.52825797,"top":0.17478053,"width":0.008976064,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.53889626,"top":0.17318435,"width":0.00731383,"height":0.018355945},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.5462101,"top":0.17318435,"width":0.006981383,"height":0.018355945},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Console\\Commands;\n\nuse Carbon\\Carbon;\nuse Carbon\\CarbonImmutable;\nuse Illuminate\\Console\\Command;\nuse Illuminate\\Support\\Facades\\Redis;\nuse InvalidArgumentException;\nuse Jiminny\\Jobs\\AutomatedReports\\RequestGenerateAskJiminnyReportJob;\nuse Jiminny\\Jobs\\AutomatedReports\\SendReportMailJob;\nuse Jiminny\\Jobs\\Crm\\Delete\\VerifyActivityCrmTaskJob;\nuse Jiminny\\Jobs\\Crm\\MatchActivityCrmData;\nuse Jiminny\\Jobs\\JobDispatcherInterface;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\AutomatedReportResult;\nuse Jiminny\\Models\\Team;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Repositories\\AutomatedReportsRepository;\nuse Jiminny\\Services\\Activity\\CrmOwnerResolver;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\n\n/**\n * Class JiminnyDebugCommand\n *\n * @package Jiminny\\Console\\Commands\n */\nclass JiminnyDebugCommand extends Command\n{\n public const string FREQUENCY_DAILY = 'daily';\n public const string FREQUENCY_WEEKLY = 'weekly';\n public const string FREQUENCY_MONTHLY = 'monthly';\n public const string FREQUENCY_QUARTERLY = 'quarterly';\n public const string FREQUENCY_ONE_OFF = 'one_off';\n protected $signature = 'jiminny:debug';\n\n public function handle(\n JobDispatcherInterface $jobDispatcher,\n AutomatedReportsService $automatedReportsService,\n AutomatedReportsRepository $automatedReportsRepository,\n UserPilotClient $userPilotClient\n ): void {\n // Choose ONE of the following to run, then comment out the others.\n // 1) Dispatch a storm of MatchActivityCrmData jobs against team 2\n $this->simulateMatchActivityStorm(teamId: 2, count: 100);\n\n // 2) Dispatch a storm of VerifyActivityCrmTaskJob jobs (simulates DeleteCrmEntityTrait fan-out)\n // $this->simulateVerifyTaskStorm(teamId: 2, count: 100);\n\n // 3) Inspect Redis circuit-breaker state for the team's HubSpot portal\n // $this->observeRateLimitCache(teamId: 2);\n\n // 4) Make 3 synchronous matchByName calls (foreground, hits API directly)\n // $this->rateLimit();\n exit(1);\n\n\n\n $report = AutomatedReport::find(71);\n $last = AutomatedReportResult::query()\n ->where('report_id', $report->getId())\n ->whereIn('status', [AutomatedReportResult::STATUS_DEFAULT, AutomatedReportResult::STATUS_FAILED])\n// ->where('reason', '!=', AutomatedReportResult::REASON_NOT_ENOUGH_ACTIVITIES)\n ->whereDate('created_at', CarbonImmutable::now()->toDateString())\n ->latest()\n ->first();\n\n $this->info(\"Last: {$last->getId()}\");\n\n exit(1);\n\n $user = User::find(143);\n // $count = $automatedReportsRepository->countUserReports($user);\n // $this->info(\"Count: {$count}\");\n // $count = $automatedReportsRepository->countAllUserReports($user);\n // $this->info(\"All count: {$count}\");\n\n $payload = [\n 'report_type' => 'ask_jiminny',\n 'frequency' => 'weekly',\n ];\n $userPilotClient->track($user, 'ask-jiminny-report-generated', $payload);\n\n exit(1);\n\n $now = Carbon::now()->subDay(1);\n $this->info(\"Now: {$now->toDateTimeString()}\");\n $weekStart = Carbon::getWeekStartsAt();\n $this->info(\"Now: {$weekStart}\");\n\n // $from = $now->copy()->previousWeekday()->startOfDay();\n // $to = $now->copy()->previousWeekday()->endOfDay();\n\n // $fromOld = $now->copy()->subWeeks(1)->startOfDay();\n // $toOld = $now->copy()->subDay()->endOfDay();\n // $fromNew = $now->copy()->subWeek()->startOfWeek();\n // $toNew = $now->copy()->subWeek()->endOfWeek();\n\n // $fromOld = $now->copy()->subMonths(1)->startOfDay();\n // $toOld = $now->copy()->subDay()->endOfDay();\n // $fromNew = $now->copy()->subMonthNoOverflow()->startOfMonth();\n // $toNew = $now->copy()->subMonthNoOverflow()->endOfMonth();\n\n $fromOld = $now->copy()->subMonths(3)->startOfDay();\n $toOld = $now->copy()->subDay()->endOfDay();\n $fromNew = $now->copy()->subQuarterNoOverflow()->startOfQuarter();\n $toNew = $now->copy()->subQuarterNoOverflow()->endOfQuarter();\n\n $this->info(\"From old: {$fromOld->toDateTimeString()}\");\n $this->info(\"To old: {$toOld->toDateTimeString()}\");\n $this->info(\"From new: {$fromNew->toDateTimeString()}\");\n $this->info(\"To new: {$toNew->toDateTimeString()}\");\n\n exit(1);\n\n $report = AutomatedReport::find(71);\n\n $job = new RequestGenerateAskJiminnyReportJob($report->getUuid());\n $jobDispatcher->dispatch($job);\n\n exit(1);\n\n\n // $this->formatDate($jobDispatcher);\n // $this->sendMail($jobDispatcher, $automatedReportsService);\n // $this->crmService();\n\n $this->getPayload($automatedReportsService);\n\n exit(1);\n }\n\n\n\n private function crmService()\n {\n $activity = Activity::find(418141);\n\n $team = Team::find(19);\n $config = $team->getCrmConfiguration();\n\n $crmResolver = app(CrmOwnerResolver::class, [\n 'team' => $team,\n 'integrationAdmin' => $team->getOwner(),\n 'providerSlug' => $config->getProviderName(),\n ]);\n\n $crmService = $crmResolver->prepareCrmService();\n\n $crmService->createTranscriptNotes($activity);\n }\n\n private function sendMail(JobDispatcherInterface $jobDispatcher, AutomatedReportsService $automatedReportsService)\n {\n $reportUuid = '';\n // $report = $automatedReportsService->getReportResult($reportUuid);\n $report = AutomatedReportResult::find(275);\n $validRecipients = $automatedReportsService->getValidRecipientUsers(\n $report->getReport(),\n includeJiminny: true,\n );\n\n $recipient = $validRecipients[0];\n\n $fileName = $automatedReportsService->getReportFileName($report);\n $typeName = $report->getReport()->getCustomName()\n ?? $automatedReportsService->getReportTypeName($report);\n $teamsName = $automatedReportsService->getReportTeamsName($report);\n $periodName = $automatedReportsService->getReportPeriodName($report);\n $s3Path = $automatedReportsService->getMediaPath($report);\n\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$fileName ' . PHP_EOL . print_r($fileName, true));\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$typeName ' . PHP_EOL . print_r($typeName, true));\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$teamsName ' . PHP_EOL . print_r($teamsName, true));\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$periodName ' . PHP_EOL . print_r($periodName, true));\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$s3Path ' . PHP_EOL . print_r($s3Path, true));\n\n $jobDispatcher->dispatch(\n new SendReportMailJob(\n reportUuid: $report->getUuid(),\n s3Path: $s3Path,\n recipientEmail: $recipient['email'],\n recipientName: $recipient['name'] ?? null,\n fileName: $fileName,\n typeName: $typeName,\n teamsName: $teamsName,\n periodName: $periodName,\n isAskJiminny: true,\n )\n );\n\n exit(1);\n }\n\n private function formatDate(JobDispatcherInterface $jobDispatcher): void\n {\n $customName = 'Custom report name';\n // $frequency = self::FREQUENCY_DAILY;\n // $frequency = self::FREQUENCY_WEEKLY;\n $frequency = self::FREQUENCY_MONTHLY;\n // $frequency = self::FREQUENCY_QUARTERLY;\n // $frequency = self::FREQUENCY_ONE_OFF;\n $period = $this->calculateFromAndToDatePeriod($frequency);\n $from = $period['fromDate'];\n $to = $period['toDate'];\n $periodName = $this->formatReportPeriodName($frequency, $from, $to);\n $filenameSuffix = null;\n\n if ($customName) {\n if ($filenameSuffix) {\n $customName .= \" {$filenameSuffix}\";\n }\n\n $result = $this->sanitizeFileName(\"{$customName} - {$periodName}\");\n }\n\n $this->info($result);\n }\n\n public function calculateFromAndToDatePeriod(\n string $frequency,\n ?Carbon $fromDate = null,\n ?Carbon $toDate = null\n ): array {\n if ($frequency === self::FREQUENCY_ONE_OFF) {\n return [\n 'fromDate' => $fromDate,\n 'toDate' => $toDate,\n ];\n }\n\n $now = Carbon::now();\n\n return match ($frequency) {\n self::FREQUENCY_DAILY => [\n 'fromDate' => $now->copy()->subDay()->startOfDay(),\n 'toDate' => $now->copy()->subDay()->endOfDay(),\n ],\n self::FREQUENCY_WEEKLY => [\n 'fromDate' => $now->copy()->subWeeks(1)->startOfDay(),\n 'toDate' => $now->copy()->subDay()->endOfDay(),\n ],\n self::FREQUENCY_MONTHLY => [\n 'fromDate' => $now->copy()->subMonths(1)->startOfDay(),\n 'toDate' => $now->copy()->subDay()->endOfDay(),\n ],\n self::FREQUENCY_QUARTERLY => [\n 'fromDate' => $now->copy()->subMonths(3)->startOfDay(),\n 'toDate' => $now->copy()->subDay()->endOfDay(),\n ],\n default => throw new InvalidArgumentException(\"Unsupported frequency: {$frequency}\"),\n };\n }\n\n private function formatReportPeriodName(string $frequency, Carbon $from, Carbon $to): string\n {\n $fromYear = $from->format('Y');\n $toYear = $to->format('Y');\n $differentYears = $fromYear !== $toYear;\n\n switch ($frequency) {\n case self::FREQUENCY_DAILY:\n return $from->format('j M Y');\n\n case self::FREQUENCY_QUARTERLY:\n // 'Jan-Mar 2025' or 'Nov 2024-Jan 2025' if years differ\n $startMonth = $from->format('M');\n $endMonth = $to->copy()->subMonth();\n $endMonthName = $endMonth->format('M');\n $endMonthYear = $endMonth->format('Y');\n\n if ($differentYears) {\n return \"{$startMonth} {$fromYear} - {$endMonthName} {$endMonthYear}\";\n }\n\n return \"{$startMonth} - {$endMonthName} {$toYear}\";\n\n case self::FREQUENCY_MONTHLY:\n // 'May 2025' - monthly reports are always within the same year\n return $from->format('M Y');\n\n case self::FREQUENCY_WEEKLY:\n // '4 - 8 Aug 2025', '27 Oct - 3 Nov 2025', or '28 Dec 2024 - 3 Jan 2025' if years differ\n $startDay = $from->format('j');\n $endDay = $to->format('j');\n $startMonth = $from->format('M');\n $endMonth = $to->format('M');\n\n if ($differentYears) {\n return \"{$startDay} {$startMonth} {$fromYear} - {$endDay} {$endMonth} {$toYear}\";\n }\n\n if ($startMonth !== $endMonth) {\n return \"{$startDay} {$startMonth} - {$endDay} {$endMonth} {$toYear}\";\n }\n\n return \"{$startDay} - {$endDay} {$endMonth} {$toYear}\";\n\n case self::FREQUENCY_ONE_OFF:\n // '2 May-31 May 2025' or '15 Dec 2024-15 Jan 2025' if years differ\n $startDay = $from->format('j');\n $startMonth = $from->format('M');\n $endDay = $to->format('j');\n $endMonth = $to->format('M');\n\n // If same month and year, use a format like '2-31 May 2025'\n if ($startMonth === $endMonth && ! $differentYears) {\n return \"{$startDay} - {$endDay} {$startMonth} {$toYear}\";\n }\n\n // If different years, include both years\n if ($differentYears) {\n return \"{$startDay} {$startMonth} {$fromYear} - {$endDay} {$endMonth} {$toYear}\";\n }\n\n // Same year but different months\n return \"{$startDay} {$startMonth} - {$endDay} {$endMonth} {$toYear}\";\n\n default:\n // Default format for unknown frequencies\n return $from->format('j M Y') . ' - ' . $to->format('j M Y');\n }\n }\n\n public function sanitizeFileName(string $fileName): string\n {\n return str_replace(['/', '\\\\'], '-', $fileName);\n }\n\n private function getPayload(AutomatedReportsService $automatedReportsService)\n {\n $reportResult = AutomatedReportResult::find(269);\n $automatedReport = $reportResult->getReport();\n $activityIds = [1,2,3];\n $payload = $automatedReportsService->getAskJiminnyGenerateReportPayload(\n automatedReport: $automatedReport,\n reportResult: $reportResult,\n activityIds: $activityIds,\n );\n\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$payload ' . PHP_EOL . print_r($payload, true));\n }\n\n private function rateLimit()\n {\n $team = Team::find(2);\n $config = $team->getCrmConfiguration();\n\n $crmResolver = app(CrmOwnerResolver::class, [\n 'team' => $team,\n 'integrationAdmin' => $team->getOwner(),\n 'providerSlug' => $config->getProviderName(),\n ]);\n\n $crmService = $crmResolver->prepareCrmService();\n\n for ($i = 0 ; $i < 3; $i++) {\n// if ($i % 25 === 0) {\n// $this->info(\"Syncing opportunity {$i}\");\n $this->info(\"Matching contact {$i}\");\n// }\n// $crmService->syncOpportunity('374720564');\n $crmService->matchByName('Robot');\n }\n }\n\n private function simulateMatchActivityStorm(int $teamId = 2, int $count = 100): void\n {\n $team = Team::find($teamId);\n $config = $team->getCrmConfiguration();\n\n $activities = Activity::query()\n ->where('crm_configuration_id', $config->getId())\n ->orderByDesc('id')\n ->limit($count)\n ->get();\n\n $this->info(\"Dispatching {$activities->count()} MatchActivityCrmData jobs (portal={$config->getId()})\");\n\n foreach ($activities as $activity) {\n MatchActivityCrmData::dispatch($activity->getId(), $config, true);\n }\n\n $this->info('Done. Watch logs and run jiminny:debug observeRateLimit to inspect cache state.');\n }\n\n private function simulateVerifyTaskStorm(int $teamId = 2, int $count = 100): void\n {\n $activities = Activity::query()\n ->where('team_id', $teamId)\n ->whereNotNull('crm_provider_id')\n ->orderByDesc('id')\n ->limit($count)\n ->get();\n\n $this->info(\"Dispatching {$activities->count()} VerifyActivityCrmTaskJob jobs\");\n\n foreach ($activities as $activity) {\n VerifyActivityCrmTaskJob::dispatch($activity->getId());\n }\n\n $this->info('Done.');\n }\n\n private function observeRateLimitCache(int $teamId = 2): void\n {\n $team = Team::find($teamId);\n $config = $team->getCrmConfiguration();\n $key = sprintf('hubspot:ratelimit:portal:%d', $config->getId());\n\n $value = Redis::get($key);\n $ttl = Redis::ttl($key);\n\n $this->info(\"Redis key: {$key}\");\n $this->info('Value: ' . ($value ?? '(empty)'));\n $this->info(\"TTL: {$ttl}s\");\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Console\\Commands;\n\nuse Carbon\\Carbon;\nuse Carbon\\CarbonImmutable;\nuse Illuminate\\Console\\Command;\nuse Illuminate\\Support\\Facades\\Redis;\nuse InvalidArgumentException;\nuse Jiminny\\Jobs\\AutomatedReports\\RequestGenerateAskJiminnyReportJob;\nuse Jiminny\\Jobs\\AutomatedReports\\SendReportMailJob;\nuse Jiminny\\Jobs\\Crm\\Delete\\VerifyActivityCrmTaskJob;\nuse Jiminny\\Jobs\\Crm\\MatchActivityCrmData;\nuse Jiminny\\Jobs\\JobDispatcherInterface;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\AutomatedReportResult;\nuse Jiminny\\Models\\Team;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Repositories\\AutomatedReportsRepository;\nuse Jiminny\\Services\\Activity\\CrmOwnerResolver;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\n\n/**\n * Class JiminnyDebugCommand\n *\n * @package Jiminny\\Console\\Commands\n */\nclass JiminnyDebugCommand extends Command\n{\n public const string FREQUENCY_DAILY = 'daily';\n public const string FREQUENCY_WEEKLY = 'weekly';\n public const string FREQUENCY_MONTHLY = 'monthly';\n public const string FREQUENCY_QUARTERLY = 'quarterly';\n public const string FREQUENCY_ONE_OFF = 'one_off';\n protected $signature = 'jiminny:debug';\n\n public function handle(\n JobDispatcherInterface $jobDispatcher,\n AutomatedReportsService $automatedReportsService,\n AutomatedReportsRepository $automatedReportsRepository,\n UserPilotClient $userPilotClient\n ): void {\n // Choose ONE of the following to run, then comment out the others.\n // 1) Dispatch a storm of MatchActivityCrmData jobs against team 2\n $this->simulateMatchActivityStorm(teamId: 2, count: 100);\n\n // 2) Dispatch a storm of VerifyActivityCrmTaskJob jobs (simulates DeleteCrmEntityTrait fan-out)\n // $this->simulateVerifyTaskStorm(teamId: 2, count: 100);\n\n // 3) Inspect Redis circuit-breaker state for the team's HubSpot portal\n // $this->observeRateLimitCache(teamId: 2);\n\n // 4) Make 3 synchronous matchByName calls (foreground, hits API directly)\n // $this->rateLimit();\n exit(1);\n\n\n\n $report = AutomatedReport::find(71);\n $last = AutomatedReportResult::query()\n ->where('report_id', $report->getId())\n ->whereIn('status', [AutomatedReportResult::STATUS_DEFAULT, AutomatedReportResult::STATUS_FAILED])\n// ->where('reason', '!=', AutomatedReportResult::REASON_NOT_ENOUGH_ACTIVITIES)\n ->whereDate('created_at', CarbonImmutable::now()->toDateString())\n ->latest()\n ->first();\n\n $this->info(\"Last: {$last->getId()}\");\n\n exit(1);\n\n $user = User::find(143);\n // $count = $automatedReportsRepository->countUserReports($user);\n // $this->info(\"Count: {$count}\");\n // $count = $automatedReportsRepository->countAllUserReports($user);\n // $this->info(\"All count: {$count}\");\n\n $payload = [\n 'report_type' => 'ask_jiminny',\n 'frequency' => 'weekly',\n ];\n $userPilotClient->track($user, 'ask-jiminny-report-generated', $payload);\n\n exit(1);\n\n $now = Carbon::now()->subDay(1);\n $this->info(\"Now: {$now->toDateTimeString()}\");\n $weekStart = Carbon::getWeekStartsAt();\n $this->info(\"Now: {$weekStart}\");\n\n // $from = $now->copy()->previousWeekday()->startOfDay();\n // $to = $now->copy()->previousWeekday()->endOfDay();\n\n // $fromOld = $now->copy()->subWeeks(1)->startOfDay();\n // $toOld = $now->copy()->subDay()->endOfDay();\n // $fromNew = $now->copy()->subWeek()->startOfWeek();\n // $toNew = $now->copy()->subWeek()->endOfWeek();\n\n // $fromOld = $now->copy()->subMonths(1)->startOfDay();\n // $toOld = $now->copy()->subDay()->endOfDay();\n // $fromNew = $now->copy()->subMonthNoOverflow()->startOfMonth();\n // $toNew = $now->copy()->subMonthNoOverflow()->endOfMonth();\n\n $fromOld = $now->copy()->subMonths(3)->startOfDay();\n $toOld = $now->copy()->subDay()->endOfDay();\n $fromNew = $now->copy()->subQuarterNoOverflow()->startOfQuarter();\n $toNew = $now->copy()->subQuarterNoOverflow()->endOfQuarter();\n\n $this->info(\"From old: {$fromOld->toDateTimeString()}\");\n $this->info(\"To old: {$toOld->toDateTimeString()}\");\n $this->info(\"From new: {$fromNew->toDateTimeString()}\");\n $this->info(\"To new: {$toNew->toDateTimeString()}\");\n\n exit(1);\n\n $report = AutomatedReport::find(71);\n\n $job = new RequestGenerateAskJiminnyReportJob($report->getUuid());\n $jobDispatcher->dispatch($job);\n\n exit(1);\n\n\n // $this->formatDate($jobDispatcher);\n // $this->sendMail($jobDispatcher, $automatedReportsService);\n // $this->crmService();\n\n $this->getPayload($automatedReportsService);\n\n exit(1);\n }\n\n\n\n private function crmService()\n {\n $activity = Activity::find(418141);\n\n $team = Team::find(19);\n $config = $team->getCrmConfiguration();\n\n $crmResolver = app(CrmOwnerResolver::class, [\n 'team' => $team,\n 'integrationAdmin' => $team->getOwner(),\n 'providerSlug' => $config->getProviderName(),\n ]);\n\n $crmService = $crmResolver->prepareCrmService();\n\n $crmService->createTranscriptNotes($activity);\n }\n\n private function sendMail(JobDispatcherInterface $jobDispatcher, AutomatedReportsService $automatedReportsService)\n {\n $reportUuid = '';\n // $report = $automatedReportsService->getReportResult($reportUuid);\n $report = AutomatedReportResult::find(275);\n $validRecipients = $automatedReportsService->getValidRecipientUsers(\n $report->getReport(),\n includeJiminny: true,\n );\n\n $recipient = $validRecipients[0];\n\n $fileName = $automatedReportsService->getReportFileName($report);\n $typeName = $report->getReport()->getCustomName()\n ?? $automatedReportsService->getReportTypeName($report);\n $teamsName = $automatedReportsService->getReportTeamsName($report);\n $periodName = $automatedReportsService->getReportPeriodName($report);\n $s3Path = $automatedReportsService->getMediaPath($report);\n\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$fileName ' . PHP_EOL . print_r($fileName, true));\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$typeName ' . PHP_EOL . print_r($typeName, true));\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$teamsName ' . PHP_EOL . print_r($teamsName, true));\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$periodName ' . PHP_EOL . print_r($periodName, true));\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$s3Path ' . PHP_EOL . print_r($s3Path, true));\n\n $jobDispatcher->dispatch(\n new SendReportMailJob(\n reportUuid: $report->getUuid(),\n s3Path: $s3Path,\n recipientEmail: $recipient['email'],\n recipientName: $recipient['name'] ?? null,\n fileName: $fileName,\n typeName: $typeName,\n teamsName: $teamsName,\n periodName: $periodName,\n isAskJiminny: true,\n )\n );\n\n exit(1);\n }\n\n private function formatDate(JobDispatcherInterface $jobDispatcher): void\n {\n $customName = 'Custom report name';\n // $frequency = self::FREQUENCY_DAILY;\n // $frequency = self::FREQUENCY_WEEKLY;\n $frequency = self::FREQUENCY_MONTHLY;\n // $frequency = self::FREQUENCY_QUARTERLY;\n // $frequency = self::FREQUENCY_ONE_OFF;\n $period = $this->calculateFromAndToDatePeriod($frequency);\n $from = $period['fromDate'];\n $to = $period['toDate'];\n $periodName = $this->formatReportPeriodName($frequency, $from, $to);\n $filenameSuffix = null;\n\n if ($customName) {\n if ($filenameSuffix) {\n $customName .= \" {$filenameSuffix}\";\n }\n\n $result = $this->sanitizeFileName(\"{$customName} - {$periodName}\");\n }\n\n $this->info($result);\n }\n\n public function calculateFromAndToDatePeriod(\n string $frequency,\n ?Carbon $fromDate = null,\n ?Carbon $toDate = null\n ): array {\n if ($frequency === self::FREQUENCY_ONE_OFF) {\n return [\n 'fromDate' => $fromDate,\n 'toDate' => $toDate,\n ];\n }\n\n $now = Carbon::now();\n\n return match ($frequency) {\n self::FREQUENCY_DAILY => [\n 'fromDate' => $now->copy()->subDay()->startOfDay(),\n 'toDate' => $now->copy()->subDay()->endOfDay(),\n ],\n self::FREQUENCY_WEEKLY => [\n 'fromDate' => $now->copy()->subWeeks(1)->startOfDay(),\n 'toDate' => $now->copy()->subDay()->endOfDay(),\n ],\n self::FREQUENCY_MONTHLY => [\n 'fromDate' => $now->copy()->subMonths(1)->startOfDay(),\n 'toDate' => $now->copy()->subDay()->endOfDay(),\n ],\n self::FREQUENCY_QUARTERLY => [\n 'fromDate' => $now->copy()->subMonths(3)->startOfDay(),\n 'toDate' => $now->copy()->subDay()->endOfDay(),\n ],\n default => throw new InvalidArgumentException(\"Unsupported frequency: {$frequency}\"),\n };\n }\n\n private function formatReportPeriodName(string $frequency, Carbon $from, Carbon $to): string\n {\n $fromYear = $from->format('Y');\n $toYear = $to->format('Y');\n $differentYears = $fromYear !== $toYear;\n\n switch ($frequency) {\n case self::FREQUENCY_DAILY:\n return $from->format('j M Y');\n\n case self::FREQUENCY_QUARTERLY:\n // 'Jan-Mar 2025' or 'Nov 2024-Jan 2025' if years differ\n $startMonth = $from->format('M');\n $endMonth = $to->copy()->subMonth();\n $endMonthName = $endMonth->format('M');\n $endMonthYear = $endMonth->format('Y');\n\n if ($differentYears) {\n return \"{$startMonth} {$fromYear} - {$endMonthName} {$endMonthYear}\";\n }\n\n return \"{$startMonth} - {$endMonthName} {$toYear}\";\n\n case self::FREQUENCY_MONTHLY:\n // 'May 2025' - monthly reports are always within the same year\n return $from->format('M Y');\n\n case self::FREQUENCY_WEEKLY:\n // '4 - 8 Aug 2025', '27 Oct - 3 Nov 2025', or '28 Dec 2024 - 3 Jan 2025' if years differ\n $startDay = $from->format('j');\n $endDay = $to->format('j');\n $startMonth = $from->format('M');\n $endMonth = $to->format('M');\n\n if ($differentYears) {\n return \"{$startDay} {$startMonth} {$fromYear} - {$endDay} {$endMonth} {$toYear}\";\n }\n\n if ($startMonth !== $endMonth) {\n return \"{$startDay} {$startMonth} - {$endDay} {$endMonth} {$toYear}\";\n }\n\n return \"{$startDay} - {$endDay} {$endMonth} {$toYear}\";\n\n case self::FREQUENCY_ONE_OFF:\n // '2 May-31 May 2025' or '15 Dec 2024-15 Jan 2025' if years differ\n $startDay = $from->format('j');\n $startMonth = $from->format('M');\n $endDay = $to->format('j');\n $endMonth = $to->format('M');\n\n // If same month and year, use a format like '2-31 May 2025'\n if ($startMonth === $endMonth && ! $differentYears) {\n return \"{$startDay} - {$endDay} {$startMonth} {$toYear}\";\n }\n\n // If different years, include both years\n if ($differentYears) {\n return \"{$startDay} {$startMonth} {$fromYear} - {$endDay} {$endMonth} {$toYear}\";\n }\n\n // Same year but different months\n return \"{$startDay} {$startMonth} - {$endDay} {$endMonth} {$toYear}\";\n\n default:\n // Default format for unknown frequencies\n return $from->format('j M Y') . ' - ' . $to->format('j M Y');\n }\n }\n\n public function sanitizeFileName(string $fileName): string\n {\n return str_replace(['/', '\\\\'], '-', $fileName);\n }\n\n private function getPayload(AutomatedReportsService $automatedReportsService)\n {\n $reportResult = AutomatedReportResult::find(269);\n $automatedReport = $reportResult->getReport();\n $activityIds = [1,2,3];\n $payload = $automatedReportsService->getAskJiminnyGenerateReportPayload(\n automatedReport: $automatedReport,\n reportResult: $reportResult,\n activityIds: $activityIds,\n );\n\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$payload ' . PHP_EOL . print_r($payload, true));\n }\n\n private function rateLimit()\n {\n $team = Team::find(2);\n $config = $team->getCrmConfiguration();\n\n $crmResolver = app(CrmOwnerResolver::class, [\n 'team' => $team,\n 'integrationAdmin' => $team->getOwner(),\n 'providerSlug' => $config->getProviderName(),\n ]);\n\n $crmService = $crmResolver->prepareCrmService();\n\n for ($i = 0 ; $i < 3; $i++) {\n// if ($i % 25 === 0) {\n// $this->info(\"Syncing opportunity {$i}\");\n $this->info(\"Matching contact {$i}\");\n// }\n// $crmService->syncOpportunity('374720564');\n $crmService->matchByName('Robot');\n }\n }\n\n private function simulateMatchActivityStorm(int $teamId = 2, int $count = 100): void\n {\n $team = Team::find($teamId);\n $config = $team->getCrmConfiguration();\n\n $activities = Activity::query()\n ->where('crm_configuration_id', $config->getId())\n ->orderByDesc('id')\n ->limit($count)\n ->get();\n\n $this->info(\"Dispatching {$activities->count()} MatchActivityCrmData jobs (portal={$config->getId()})\");\n\n foreach ($activities as $activity) {\n MatchActivityCrmData::dispatch($activity->getId(), $config, true);\n }\n\n $this->info('Done. Watch logs and run jiminny:debug observeRateLimit to inspect cache state.');\n }\n\n private function simulateVerifyTaskStorm(int $teamId = 2, int $count = 100): void\n {\n $activities = Activity::query()\n ->where('team_id', $teamId)\n ->whereNotNull('crm_provider_id')\n ->orderByDesc('id')\n ->limit($count)\n ->get();\n\n $this->info(\"Dispatching {$activities->count()} VerifyActivityCrmTaskJob jobs\");\n\n foreach ($activities as $activity) {\n VerifyActivityCrmTaskJob::dispatch($activity->getId());\n }\n\n $this->info('Done.');\n }\n\n private function observeRateLimitCache(int $teamId = 2): void\n {\n $team = Team::find($teamId);\n $config = $team->getCrmConfiguration();\n $key = sprintf('hubspot:ratelimit:portal:%d', $config->getId());\n\n $value = Redis::get($key);\n $ttl = Redis::ttl($key);\n\n $this->info(\"Redis key: {$key}\");\n $this->info('Value: ' . ($value ?? '(empty)'));\n $this->info(\"TTL: {$ttl}s\");\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Show Replace Field","depth":4,"bounds":{"left":0.5575133,"top":0.08060654,"width":0.008643617,"height":0.01915403},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Search History","depth":3,"bounds":{"left":0.57014626,"top":0.07980846,"width":0.00731383,"height":0.017557861},"on_screen":true,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"429","depth":4,"bounds":{"left":0.58111703,"top":0.07980846,"width":0.077792555,"height":0.015961692},"on_screen":true,"value":"429","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"New Line","depth":3,"bounds":{"left":0.66788566,"top":0.07980846,"width":0.00731383,"height":0.017557861},"on_screen":true,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Match Case","depth":3,"bounds":{"left":0.67785907,"top":0.07980846,"width":0.00731383,"height":0.017557861},"on_screen":true,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Words","depth":3,"bounds":{"left":0.68650264,"top":0.07980846,"width":0.00731383,"height":0.017557861},"on_screen":true,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Regex","depth":3,"bounds":{"left":0.69514626,"top":0.07980846,"width":0.00731383,"height":0.017557861},"on_screen":true,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Replace History","depth":3,"bounds":{"left":0.27027926,"top":1.0,"width":0.00731383,"height":0.0},"on_screen":false,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"Replace","depth":4,"on_screen":false,"role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"New Line","depth":3,"bounds":{"left":0.27027926,"top":1.0,"width":0.00731383,"height":0.0},"on_screen":false,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Preserve case","depth":3,"bounds":{"left":0.27027926,"top":1.0,"width":0.00731383,"height":0.0},"on_screen":false,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"0 results","depth":4,"bounds":{"left":0.7087766,"top":0.079010375,"width":0.025598405,"height":0.017557861},"on_screen":true,"role_description":"text"},{"role":"AXButton","text":"Previous Occurrence","depth":4,"bounds":{"left":0.734375,"top":0.07821229,"width":0.008643617,"height":0.01915403},"on_screen":true,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Occurrence","depth":4,"bounds":{"left":0.7430186,"top":0.07821229,"width":0.008643617,"height":0.01915403},"on_screen":true,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Filter Search Results","depth":4,"bounds":{"left":0.75166225,"top":0.07821229,"width":0.008643617,"height":0.01915403},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Open in Window, Multiple Cursors","depth":4,"bounds":{"left":0.7603058,"top":0.07821229,"width":0.008643617,"height":0.01915403},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXLink","text":"Click to highlight","depth":4,"on_screen":false,"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close","depth":4,"bounds":{"left":0.97539896,"top":0.07821229,"width":0.008643617,"height":0.01915403},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"124","depth":4,"bounds":{"left":0.96043885,"top":0.10933759,"width":0.011968086,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.9740692,"top":0.10774142,"width":0.00731383,"height":0.018355945},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.98138297,"top":0.10774142,"width":0.006981383,"height":0.018355945},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"[2026-05-11 11:06:08] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":612562,\"team_id\":2,\"email\":\"horen.kirazyan@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"c1949c5e-c0f4-431e-ba26-59d49408d871\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:08] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":612562,\"participants_processed\":2,\"exact_matches\":0,\"domain_matches\":1,\"best_match_found\":true} {\"correlation_id\":\"c1949c5e-c0f4-431e-ba26-59d49408d871\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:08] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612562} {\"correlation_id\":\"c1949c5e-c0f4-431e-ba26-59d49408d871\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:08] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612562,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"c1949c5e-c0f4-431e-ba26-59d49408d871\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:08] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":612562,\"remote_search\":true,\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89} {\"correlation_id\":\"c1949c5e-c0f4-431e-ba26-59d49408d871\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:09] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":612561,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89}} {\"correlation_id\":\"b715f531-04da-410f-be31-055aa1c13754\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:09] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612561} {\"correlation_id\":\"b715f531-04da-410f-be31-055aa1c13754\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:09] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612561,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"b715f531-04da-410f-be31-055aa1c13754\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:09] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":612561,\"participants\":[{\"id\":999780,\"user_id\":206,\"contact_id\":null,\"lead_id\":null},{\"id\":999781,\"user_id\":null,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"b715f531-04da-410f-be31-055aa1c13754\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:09] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"b715f531-04da-410f-be31-055aa1c13754\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:09] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"b715f531-04da-410f-be31-055aa1c13754\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:09] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"b715f531-04da-410f-be31-055aa1c13754\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:09] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"b715f531-04da-410f-be31-055aa1c13754\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:09] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":612561,\"team_id\":2,\"email\":\"horen.kirazyan@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"b715f531-04da-410f-be31-055aa1c13754\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:09] local.INFO: [Prospect match] Cache miss, calling the API {\"identifier_type\":\"email\",\"identifier\":\"447700174614.447782589921.OeREojLVnk@txt.staging.jiminny.com\"} {\"correlation_id\":\"b715f531-04da-410f-be31-055aa1c13754\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:10] local.INFO: [Hubspot] Failed to fetch contact {\"email\":\"447700174614.447782589921.OeREojLVnk@txt.staging.jiminny.com\",\"reason\":\"[404] Client error: `GET https://api.hubapi.com/crm/v3/objects/contacts/447700174614.447782589921.OeREojLVnk%40txt.staging.jiminny.com?properties=email%2Cfirstname%2Clastname%2Ccountry%2Cphone%2Cmobilephone%2Cjobtitle%2Chubspot_owner_id%2Cassociatedcompanyid%2Cphoto&archived=0&idProperty=email` resulted in a `404 Not Found` response\"} {\"correlation_id\":\"b715f531-04da-410f-be31-055aa1c13754\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:10] local.INFO: [Prospect match] API returned empty result, caching the miss with empty prospect data {\"identifier_type\":\"email\",\"identifier\":\"447700174614.447782589921.OeREojLVnk@txt.staging.jiminny.com\"} {\"correlation_id\":\"b715f531-04da-410f-be31-055aa1c13754\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:10] local.INFO: [Prospect match] Resolved company domain from email {\"email\":\"447700174614.447782589921.OeREojLVnk@txt.staging.jiminny.com\",\"domain\":\"jiminny.com\"} {\"correlation_id\":\"b715f531-04da-410f-be31-055aa1c13754\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:10] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":612561,\"participants_processed\":2,\"exact_matches\":0,\"domain_matches\":1,\"best_match_found\":true} {\"correlation_id\":\"b715f531-04da-410f-be31-055aa1c13754\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:10] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612561} {\"correlation_id\":\"b715f531-04da-410f-be31-055aa1c13754\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:10] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612561,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"b715f531-04da-410f-be31-055aa1c13754\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:10] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":612561,\"remote_search\":true,\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89} {\"correlation_id\":\"b715f531-04da-410f-be31-055aa1c13754\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:10] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":612560,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89}} {\"correlation_id\":\"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:10] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612560} {\"correlation_id\":\"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:10] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612560,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:10] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":612560,\"participants\":[{\"id\":999778,\"user_id\":null,\"contact_id\":null,\"lead_id\":null},{\"id\":999779,\"user_id\":206,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"447782589921@txt.staging.jiminny.com\"} {\"correlation_id\":\"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [Prospect match] cached empty result - no API calls, try next matching method {\"identifier_type\":\"email\",\"identifier\":\"447782589921@txt.staging.jiminny.com\"} {\"correlation_id\":\"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [Prospect match] Resolved company domain from email {\"email\":\"447782589921@txt.staging.jiminny.com\",\"domain\":\"jiminny.com\"} {\"correlation_id\":\"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":612560,\"team_id\":2,\"email\":\"horen.kirazyan@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":612560,\"participants_processed\":2,\"exact_matches\":0,\"domain_matches\":1,\"best_match_found\":true} {\"correlation_id\":\"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612560} {\"correlation_id\":\"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [ EsUpdateProcessManager ] Finished updating entities in ES {\"worker\":\"\",\"peak_memory\":\"99.73 MB\",\"elapsed_seconds\":0.74,\"update_target\":\"activities\",\"should_iterate_again\":false} {\"correlation_id\":\"38935ae1-fcb3-437e-9c9e-1b7415a0b7e1\",\"trace_id\":\"325a2f9b-9c66-4223-ac02-1a43723f4315\"}\n[2026-05-11 11:06:11] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612560,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":612560,\"remote_search\":true,\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89} {\"correlation_id\":\"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":612559,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":97,\"account_id\":69,\"opportunity_id\":5011,\"stage_id\":34}} {\"correlation_id\":\"4d59a59c-38a5-4a1d-9379-6ceaf67b747e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612559} {\"correlation_id\":\"4d59a59c-38a5-4a1d-9379-6ceaf67b747e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612559,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"4d59a59c-38a5-4a1d-9379-6ceaf67b747e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":612559,\"participants\":[{\"id\":999776,\"user_id\":206,\"contact_id\":null,\"lead_id\":null},{\"id\":999777,\"user_id\":null,\"contact_id\":97,\"lead_id\":null}]} {\"correlation_id\":\"4d59a59c-38a5-4a1d-9379-6ceaf67b747e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"4d59a59c-38a5-4a1d-9379-6ceaf67b747e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"4d59a59c-38a5-4a1d-9379-6ceaf67b747e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"4d59a59c-38a5-4a1d-9379-6ceaf67b747e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"4d59a59c-38a5-4a1d-9379-6ceaf67b747e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":612559,\"team_id\":2,\"email\":\"horen.kirazyan@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"4d59a59c-38a5-4a1d-9379-6ceaf67b747e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: ProspectCache - Searching DB for opportunity by owner {\"account_id\":69,\"contact_id\":97,\"owner_id\":206} {\"correlation_id\":\"4d59a59c-38a5-4a1d-9379-6ceaf67b747e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: ProspectCache - Fallback DB opportunity search {\"account_id\":69,\"contact_id\":97} {\"correlation_id\":\"4d59a59c-38a5-4a1d-9379-6ceaf67b747e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: ProspectCache - Opportunity DB search results {\"account_id\":69,\"contact_id\":97,\"opportunity_id\":5011} {\"correlation_id\":\"4d59a59c-38a5-4a1d-9379-6ceaf67b747e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"robinsoncrusoe@test.com\"} {\"correlation_id\":\"4d59a59c-38a5-4a1d-9379-6ceaf67b747e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":612559,\"participants_processed\":2,\"exact_matches\":1,\"domain_matches\":0,\"best_match_found\":true} {\"correlation_id\":\"4d59a59c-38a5-4a1d-9379-6ceaf67b747e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612559} {\"correlation_id\":\"4d59a59c-38a5-4a1d-9379-6ceaf67b747e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612559,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"4d59a59c-38a5-4a1d-9379-6ceaf67b747e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":612559,\"remote_search\":true,\"lead_id\":null,\"contact_id\":97,\"account_id\":69,\"opportunity_id\":5011,\"stage_id\":34} {\"correlation_id\":\"4d59a59c-38a5-4a1d-9379-6ceaf67b747e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:12] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":612558,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":97,\"account_id\":69,\"opportunity_id\":5011,\"stage_id\":34}} {\"correlation_id\":\"a105800d-d2f7-4772-a534-7791e262461c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:12] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612558} {\"correlation_id\":\"a105800d-d2f7-4772-a534-7791e262461c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:12] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612558,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"a105800d-d2f7-4772-a534-7791e262461c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:12] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":612558,\"participants\":[{\"id\":999774,\"user_id\":206,\"contact_id\":null,\"lead_id\":null},{\"id\":999775,\"user_id\":null,\"contact_id\":97,\"lead_id\":null}]} {\"correlation_id\":\"a105800d-d2f7-4772-a534-7791e262461c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:12] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"a105800d-d2f7-4772-a534-7791e262461c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:12] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"a105800d-d2f7-4772-a534-7791e262461c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:12] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"a105800d-d2f7-4772-a534-7791e262461c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:12] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"a105800d-d2f7-4772-a534-7791e262461c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:12] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":612558,\"team_id\":2,\"email\":\"horen.kirazyan@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"a105800d-d2f7-4772-a534-7791e262461c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:12] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"robinsoncrusoe@test.com\"} {\"correlation_id\":\"a105800d-d2f7-4772-a534-7791e262461c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:12] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":612558,\"participants_processed\":2,\"exact_matches\":1,\"domain_matches\":0,\"best_match_found\":true} {\"correlation_id\":\"a105800d-d2f7-4772-a534-7791e262461c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:12] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612558} {\"correlation_id\":\"a105800d-d2f7-4772-a534-7791e262461c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:12] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612558,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"a105800d-d2f7-4772-a534-7791e262461c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:12] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":612558,\"remote_search\":true,\"lead_id\":null,\"contact_id\":97,\"account_id\":69,\"opportunity_id\":5011,\"stage_id\":34} {\"correlation_id\":\"a105800d-d2f7-4772-a534-7791e262461c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:13] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":612557,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":97,\"account_id\":69,\"opportunity_id\":5011,\"stage_id\":34}} {\"correlation_id\":\"3ec57437-5796-4e44-8bf0-aaec149ac813\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:13] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612557} {\"correlation_id\":\"3ec57437-5796-4e44-8bf0-aaec149ac813\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:13] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612557,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"3ec57437-5796-4e44-8bf0-aaec149ac813\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:13] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":612557,\"participants\":[{\"id\":999772,\"user_id\":206,\"contact_id\":null,\"lead_id\":null},{\"id\":999773,\"user_id\":null,\"contact_id\":97,\"lead_id\":null}]} {\"correlation_id\":\"3ec57437-5796-4e44-8bf0-aaec149ac813\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:13] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"mailbox:skip-lists:refresh\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.727} {\"correlation_id\":\"5203040f-23be-43e6-8410-ba49ee1b38b4\",\"trace_id\":\"e787953d-9b78-4b63-a987-4f93f6892984\"}\n[2026-05-11 11:06:13] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"3ec57437-5796-4e44-8bf0-aaec149ac813\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:13] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"3ec57437-5796-4e44-8bf0-aaec149ac813\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:13] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"3ec57437-5796-4e44-8bf0-aaec149ac813\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:13] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"3ec57437-5796-4e44-8bf0-aaec149ac813\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:13] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":612557,\"team_id\":2,\"email\":\"horen.kirazyan@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"3ec57437-5796-4e44-8bf0-aaec149ac813\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:13] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"robinsoncrusoe@test.com\"} {\"correlation_id\":\"3ec57437-5796-4e44-8bf0-aaec149ac813\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:13] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":612557,\"participants_processed\":2,\"exact_matches\":1,\"domain_matches\":0,\"best_match_found\":true} {\"correlation_id\":\"3ec57437-5796-4e44-8bf0-aaec149ac813\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:13] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612557} {\"correlation_id\":\"3ec57437-5796-4e44-8bf0-aaec149ac813\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:13] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612557,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"3ec57437-5796-4e44-8bf0-aaec149ac813\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:13] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":612557,\"remote_search\":true,\"lead_id\":null,\"contact_id\":97,\"account_id\":69,\"opportunity_id\":5011,\"stage_id\":34} {\"correlation_id\":\"3ec57437-5796-4e44-8bf0-aaec149ac813\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:13] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage for command {\"command\":\"mailbox:skip-lists:refresh\",\"memoryBeforeCommandInMb\":60.0,\"memoryAfterCommandInMB\":60.0,\"memoryPeakBeforeCommandInMb\":99.727,\"memoryPeakAfterCommandInMB\":99.727} {\"correlation_id\":\"5203040f-23be-43e6-8410-ba49ee1b38b4\",\"trace_id\":\"e787953d-9b78-4b63-a987-4f93f6892984\"}\n[2026-05-11 11:06:13] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":612556,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":97,\"account_id\":69,\"opportunity_id\":5011,\"stage_id\":34}} {\"correlation_id\":\"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:13] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612556} {\"correlation_id\":\"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:13] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612556,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:13] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":612556,\"participants\":[{\"id\":999770,\"user_id\":206,\"contact_id\":null,\"lead_id\":null},{\"id\":999771,\"user_id\":null,\"contact_id\":97,\"lead_id\":null}]} {\"correlation_id\":\"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:13] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:13] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:13] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:13] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":612556,\"team_id\":2,\"email\":\"horen.kirazyan@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"robinsoncrusoe@test.com\"} {\"correlation_id\":\"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":612556,\"participants_processed\":2,\"exact_matches\":1,\"domain_matches\":0,\"best_match_found\":true} {\"correlation_id\":\"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612556} {\"correlation_id\":\"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612556,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":612556,\"remote_search\":true,\"lead_id\":null,\"contact_id\":97,\"account_id\":69,\"opportunity_id\":5011,\"stage_id\":34} {\"correlation_id\":\"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":612555,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":97,\"account_id\":69,\"opportunity_id\":5011,\"stage_id\":34}} {\"correlation_id\":\"3e9d1a70-5491-44ae-8f28-5cb9b60e1859\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612555} {\"correlation_id\":\"3e9d1a70-5491-44ae-8f28-5cb9b60e1859\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612555,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"3e9d1a70-5491-44ae-8f28-5cb9b60e1859\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":612555,\"participants\":[{\"id\":999768,\"user_id\":206,\"contact_id\":null,\"lead_id\":null},{\"id\":999769,\"user_id\":null,\"contact_id\":97,\"lead_id\":null}]} {\"correlation_id\":\"3e9d1a70-5491-44ae-8f28-5cb9b60e1859\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"3e9d1a70-5491-44ae-8f28-5cb9b60e1859\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"3e9d1a70-5491-44ae-8f28-5cb9b60e1859\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"3e9d1a70-5491-44ae-8f28-5cb9b60e1859\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"3e9d1a70-5491-44ae-8f28-5cb9b60e1859\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":612555,\"team_id\":2,\"email\":\"horen.kirazyan@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"3e9d1a70-5491-44ae-8f28-5cb9b60e1859\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"robinsoncrusoe@test.com\"} {\"correlation_id\":\"3e9d1a70-5491-44ae-8f28-5cb9b60e1859\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":612555,\"participants_processed\":2,\"exact_matches\":1,\"domain_matches\":0,\"best_match_found\":true} {\"correlation_id\":\"3e9d1a70-5491-44ae-8f28-5cb9b60e1859\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612555} {\"correlation_id\":\"3e9d1a70-5491-44ae-8f28-5cb9b60e1859\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612555,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"3e9d1a70-5491-44ae-8f28-5cb9b60e1859\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":612555,\"remote_search\":true,\"lead_id\":null,\"contact_id\":97,\"account_id\":69,\"opportunity_id\":5011,\"stage_id\":34} {\"correlation_id\":\"3e9d1a70-5491-44ae-8f28-5cb9b60e1859\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":612554,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":97,\"account_id\":69,\"opportunity_id\":5011,\"stage_id\":34}} {\"correlation_id\":\"7101e979-3a7e-4b2c-bf04-dc6a24ba1356\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612554} {\"correlation_id\":\"7101e979-3a7e-4b2c-bf04-dc6a24ba1356\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612554,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"7101e979-3a7e-4b2c-bf04-dc6a24ba1356\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":612554,\"participants\":[{\"id\":999766,\"user_id\":206,\"contact_id\":null,\"lead_id\":null},{\"id\":999767,\"user_id\":null,\"contact_id\":97,\"lead_id\":null}]} {\"correlation_id\":\"7101e979-3a7e-4b2c-bf04-dc6a24ba1356\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"7101e979-3a7e-4b2c-bf04-dc6a24ba1356\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"7101e979-3a7e-4b2c-bf04-dc6a24ba1356\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"7101e979-3a7e-4b2c-bf04-dc6a24ba1356\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"7101e979-3a7e-4b2c-bf04-dc6a24ba1356\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":612554,\"team_id\":2,\"email\":\"horen.kirazyan@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"7101e979-3a7e-4b2c-bf04-dc6a24ba1356\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"robinsoncrusoe@test.com\"} {\"correlation_id\":\"7101e979-3a7e-4b2c-bf04-dc6a24ba1356\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":612554,\"participants_processed\":2,\"exact_matches\":1,\"domain_matches\":0,\"best_match_found\":true} {\"correlation_id\":\"7101e979-3a7e-4b2c-bf04-dc6a24ba1356\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612554} {\"correlation_id\":\"7101e979-3a7e-4b2c-bf04-dc6a24ba1356\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612554,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"7101e979-3a7e-4b2c-bf04-dc6a24ba1356\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":612554,\"remote_search\":true,\"lead_id\":null,\"contact_id\":97,\"account_id\":69,\"opportunity_id\":5011,\"stage_id\":34} {\"correlation_id\":\"7101e979-3a7e-4b2c-bf04-dc6a24ba1356\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":612553,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":97,\"account_id\":69,\"opportunity_id\":5011,\"stage_id\":34}} {\"correlation_id\":\"346dc229-757b-496c-aceb-d2ddb367c7d6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612553} {\"correlation_id\":\"346dc229-757b-496c-aceb-d2ddb367c7d6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612553,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"346dc229-757b-496c-aceb-d2ddb367c7d6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":612553,\"participants\":[{\"id\":999764,\"user_id\":206,\"contact_id\":null,\"lead_id\":null},{\"id\":999765,\"user_id\":null,\"contact_id\":97,\"lead_id\":null}]} {\"correlation_id\":\"346dc229-757b-496c-aceb-d2ddb367c7d6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"346dc229-757b-496c-aceb-d2ddb367c7d6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"346dc229-757b-496c-aceb-d2ddb367c7d6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"346dc229-757b-496c-aceb-d2ddb367c7d6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"346dc229-757b-496c-aceb-d2ddb367c7d6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":612553,\"team_id\":2,\"email\":\"horen.kirazyan@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"346dc229-757b-496c-aceb-d2ddb367c7d6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"robinsoncrusoe@test.com\"} {\"correlation_id\":\"346dc229-757b-496c-aceb-d2ddb367c7d6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":612553,\"participants_processed\":2,\"exact_matches\":1,\"domain_matches\":0,\"best_match_found\":true} {\"correlation_id\":\"346dc229-757b-496c-aceb-d2ddb367c7d6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612553} {\"correlation_id\":\"346dc229-757b-496c-aceb-d2ddb367c7d6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612553,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"346dc229-757b-496c-aceb-d2ddb367c7d6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":612553,\"remote_search\":true,\"lead_id\":null,\"contact_id\":97,\"account_id\":69,\"opportunity_id\":5011,\"stage_id\":34} {\"correlation_id\":\"346dc229-757b-496c-aceb-d2ddb367c7d6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":612552,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":97,\"account_id\":69,\"opportunity_id\":5011,\"stage_id\":34}} {\"correlation_id\":\"56ef8656-0bfa-4f11-a245-9254b8179555\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612552} {\"correlation_id\":\"56ef8656-0bfa-4f11-a245-9254b8179555\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612552,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"56ef8656-0bfa-4f11-a245-9254b8179555\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":612552,\"participants\":[{\"id\":999762,\"user_id\":206,\"contact_id\":null,\"lead_id\":null},{\"id\":999763,\"user_id\":null,\"contact_id\":97,\"lead_id\":null}]} {\"correlation_id\":\"56ef8656-0bfa-4f11-a245-9254b8179555\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"56ef8656-0bfa-4f11-a245-9254b8179555\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"56ef8656-0bfa-4f11-a245-9254b8179555\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"56ef8656-0bfa-4f11-a245-9254b8179555\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"56ef8656-0bfa-4f11-a245-9254b8179555\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":612552,\"team_id\":2,\"email\":\"horen.kirazyan@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"56ef8656-0bfa-4f11-a245-9254b8179555\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"robinsoncrusoe@test.com\"} {\"correlation_id\":\"56ef8656-0bfa-4f11-a245-9254b8179555\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":612552,\"participants_processed\":2,\"exact_matches\":1,\"domain_matches\":0,\"best_match_found\":true} {\"correlation_id\":\"56ef8656-0bfa-4f11-a245-9254b8179555\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612552} {\"correlation_id\":\"56ef8656-0bfa-4f11-a245-9254b8179555\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612552,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"56ef8656-0bfa-4f11-a245-9254b8179555\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":612552,\"remote_search\":true,\"lead_id\":null,\"contact_id\":97,\"account_id\":69,\"opportunity_id\":5011,\"stage_id\":34} {\"correlation_id\":\"56ef8656-0bfa-4f11-a245-9254b8179555\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":612551,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":97,\"account_id\":69,\"opportunity_id\":5011,\"stage_id\":34}} {\"correlation_id\":\"df5e35c3-b4ae-4eba-9108-cd30ac152335\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612551} {\"correlation_id\":\"df5e35c3-b4ae-4eba-9108-cd30ac152335\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612551,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"df5e35c3-b4ae-4eba-9108-cd30ac152335\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":612551,\"participants\":[{\"id\":999760,\"user_id\":206,\"contact_id\":null,\"lead_id\":null},{\"id\":999761,\"user_id\":null,\"contact_id\":97,\"lead_id\":null}]} {\"correlation_id\":\"df5e35c3-b4ae-4eba-9108-cd30ac152335\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"df5e35c3-b4ae-4eba-9108-cd30ac152335\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"df5e35c3-b4ae-4eba-9108-cd30ac152335\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"df5e35c3-b4ae-4eba-9108-cd30ac152335\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"df5e35c3-b4ae-4eba-9108-cd30ac152335\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":612551,\"team_id\":2,\"email\":\"horen.kirazyan@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"df5e35c3-b4ae-4eba-9108-cd30ac152335\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"robinsoncrusoe@test.com\"} {\"correlation_id\":\"df5e35c3-b4ae-4eba-9108-cd30ac152335\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":612551,\"participants_processed\":2,\"exact_matches\":1,\"domain_matches\":0,\"best_match_found\":true} {\"correlation_id\":\"df5e35c3-b4ae-4eba-9108-cd30ac152335\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612551} {\"correlation_id\":\"df5e35c3-b4ae-4eba-9108-cd30ac152335\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612551,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"df5e35c3-b4ae-4eba-9108-cd30ac152335\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":612551,\"remote_search\":true,\"lead_id\":null,\"contact_id\":97,\"account_id\":69,\"opportunity_id\":5011,\"stage_id\":34} {\"correlation_id\":\"df5e35c3-b4ae-4eba-9108-cd30ac152335\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":612550,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":97,\"account_id\":69,\"opportunity_id\":5011,\"stage_id\":34}} {\"correlation_id\":\"64768905-983e-4f4e-a368-901931486f34\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612550} {\"correlation_id\":\"64768905-983e-4f4e-a368-901931486f34\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612550,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"64768905-983e-4f4e-a368-901931486f34\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":612550,\"participants\":[{\"id\":999758,\"user_id\":206,\"contact_id\":null,\"lead_id\":null},{\"id\":999759,\"user_id\":null,\"contact_id\":97,\"lead_id\":null}]} {\"correlation_id\":\"64768905-983e-4f4e-a368-901931486f34\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"64768905-983e-4f4e-a368-901931486f34\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"64768905-983e-4f4e-a368-901931486f34\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"64768905-983e-4f4e-a368-901931486f34\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"64768905-983e-4f4e-a368-901931486f34\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":612550,\"team_id\":2,\"email\":\"horen.kirazyan@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"64768905-983e-4f4e-a368-901931486f34\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"robinsoncrusoe@test.com\"} {\"correlation_id\":\"64768905-983e-4f4e-a368-901931486f34\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":612550,\"participants_processed\":2,\"exact_matches\":1,\"domain_matches\":0,\"best_match_found\":true} {\"correlation_id\":\"64768905-983e-4f4e-a368-901931486f34\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612550} {\"correlation_id\":\"64768905-983e-4f4e-a368-901931486f34\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612550,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"64768905-983e-4f4e-a368-901931486f34\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":612550,\"remote_search\":true,\"lead_id\":null,\"contact_id\":97,\"account_id\":69,\"opportunity_id\":5011,\"stage_id\":34} {\"correlation_id\":\"64768905-983e-4f4e-a368-901931486f34\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":612549,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":97,\"account_id\":69,\"opportunity_id\":5011,\"stage_id\":34}} {\"correlation_id\":\"97c9609f-bef0-4e95-8301-9f5549d29c1e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612549} {\"correlation_id\":\"97c9609f-bef0-4e95-8301-9f5549d29c1e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612549,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"97c9609f-bef0-4e95-8301-9f5549d29c1e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":612549,\"participants\":[{\"id\":999756,\"user_id\":206,\"contact_id\":null,\"lead_id\":null},{\"id\":999757,\"user_id\":null,\"contact_id\":97,\"lead_id\":null}]} {\"correlation_id\":\"97c9609f-bef0-4e95-8301-9f5549d29c1e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"97c9609f-bef0-4e95-8301-9f5549d29c1e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"97c9609f-bef0-4e95-8301-9f5549d29c1e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"97c9609f-bef0-4e95-8301-9f5549d29c1e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"97c9609f-bef0-4e95-8301-9f5549d29c1e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":612549,\"team_id\":2,\"email\":\"horen.kirazyan@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"97c9609f-bef0-4e95-8301-9f5549d29c1e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"robinsoncrusoe@test.com\"} {\"correlation_id\":\"97c9609f-bef0-4e95-8301-9f5549d29c1e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":612549,\"participants_processed\":2,\"exact_matches\":1,\"domain_matches\":0,\"best_match_found\":true} {\"correlation_id\":\"97c9609f-bef0-4e95-8301-9f5549d29c1e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612549} {\"correlation_id\":\"97c9609f-bef0-4e95-8301-9f5549d29c1e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612549,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"97c9609f-bef0-4e95-8301-9f5549d29c1e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":612549,\"remote_search\":true,\"lead_id\":null,\"contact_id\":97,\"account_id\":69,\"opportunity_id\":5011,\"stage_id\":34} {\"correlation_id\":\"97c9609f-bef0-4e95-8301-9f5549d29c1e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":612365,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":4491,\"account_id\":243,\"opportunity_id\":276,\"stage_id\":36}} {\"correlation_id\":\"7761b4e0-c2d4-4805-8d1d-26728cbffb07\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612365} {\"correlation_id\":\"7761b4e0-c2d4-4805-8d1d-26728cbffb07\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612365,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"7761b4e0-c2d4-4805-8d1d-26728cbffb07\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":612365,\"participants\":[{\"id\":999563,\"user_id\":null,\"contact_id\":4491,\"lead_id\":null},{\"id\":999564,\"user_id\":206,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"7761b4e0-c2d4-4805-8d1d-26728cbffb07\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"7761b4e0-c2d4-4805-8d1d-26728cbffb07\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"7761b4e0-c2d4-4805-8d1d-26728cbffb07\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"7761b4e0-c2d4-4805-8d1d-26728cbffb07\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"7761b4e0-c2d4-4805-8d1d-26728cbffb07\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"horencho@gmail.com\"} {\"correlation_id\":\"7761b4e0-c2d4-4805-8d1d-26728cbffb07\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":612365,\"team_id\":2,\"email\":\"horen.kirazyan@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"7761b4e0-c2d4-4805-8d1d-26728cbffb07\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":612365,\"participants_processed\":2,\"exact_matches\":1,\"domain_matches\":0,\"best_match_found\":true} {\"correlation_id\":\"7761b4e0-c2d4-4805-8d1d-26728cbffb07\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612365} {\"correlation_id\":\"7761b4e0-c2d4-4805-8d1d-26728cbffb07\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612365,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"7761b4e0-c2d4-4805-8d1d-26728cbffb07\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":612365,\"remote_search\":true,\"lead_id\":null,\"contact_id\":4491,\"account_id\":243,\"opportunity_id\":276,\"stage_id\":36} {\"correlation_id\":\"7761b4e0-c2d4-4805-8d1d-26728cbffb07\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":612360,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":4491,\"account_id\":243,\"opportunity_id\":276,\"stage_id\":36}} {\"correlation_id\":\"b8b554cd-4830-4ac5-be75-595cc7eb6ca8\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612360} {\"correlation_id\":\"b8b554cd-4830-4ac5-be75-595cc7eb6ca8\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612360,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"b8b554cd-4830-4ac5-be75-595cc7eb6ca8\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":612360,\"participants\":[{\"id\":999552,\"user_id\":null,\"contact_id\":4491,\"lead_id\":null},{\"id\":999553,\"user_id\":206,\"contact_id\":null,\"lead_id\":null},{\"id\":999565,\"user_id\":null,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"b8b554cd-4830-4ac5-be75-595cc7eb6ca8\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"b8b554cd-4830-4ac5-be75-595cc7eb6ca8\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"b8b554cd-4830-4ac5-be75-595cc7eb6ca8\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"b8b554cd-4830-4ac5-be75-595cc7eb6ca8\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"b8b554cd-4830-4ac5-be75-595cc7eb6ca8\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"horencho@gmail.com\"} {\"correlation_id\":\"b8b554cd-4830-4ac5-be75-595cc7eb6ca8\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":612360,\"team_id\":2,\"email\":\"horen.kirazyan@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"b8b554cd-4830-4ac5-be75-595cc7eb6ca8\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [Hubspot] Pagination completed {\"team_id\":2,\"endpoint\":\"https://api.hubapi.com/crm/v3/objects/contact/search\",\"total_requests\":1,\"total_records_fetched\":0,\"total_elapsed_seconds\":0.31,\"average_seconds_per_request\":0.31} {\"correlation_id\":\"b8b554cd-4830-4ac5-be75-595cc7eb6ca8\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":612360,\"participants_processed\":3,\"exact_matches\":1,\"domain_matches\":0,\"best_match_found\":true} {\"correlation_id\":\"b8b554cd-4830-4ac5-be75-595cc7eb6ca8\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612360} {\"correlation_id\":\"b8b554cd-4830-4ac5-be75-595cc7eb6ca8\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612360,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"b8b554cd-4830-4ac5-be75-595cc7eb6ca8\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":612360,\"remote_search\":true,\"lead_id\":null,\"contact_id\":4491,\"account_id\":243,\"opportunity_id\":276,\"stage_id\":36} {\"correlation_id\":\"b8b554cd-4830-4ac5-be75-595cc7eb6ca8\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":612340,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":4491,\"account_id\":243,\"opportunity_id\":276,\"stage_id\":36}} {\"correlation_id\":\"9a47ab79-dbb3-46e2-ad27-3a5120c080ef\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612340} {\"correlation_id\":\"9a47ab79-dbb3-46e2-ad27-3a5120c080ef\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612340,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"9a47ab79-dbb3-46e2-ad27-3a5120c080ef\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":612340,\"participants\":[{\"id\":999516,\"user_id\":null,\"contact_id\":4491,\"lead_id\":null},{\"id\":999517,\"user_id\":206,\"contact_id\":null,\"lead_id\":null},{\"id\":999518,\"user_id\":null,\"contact_id\":null,\"lead_id\":null},{\"id\":999519,\"user_id\":null,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"9a47ab79-dbb3-46e2-ad27-3a5120c080ef\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"9a47ab79-dbb3-46e2-ad27-3a5120c080ef\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"9a47ab79-dbb3-46e2-ad27-3a5120c080ef\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"9a47ab79-dbb3-46e2-ad27-3a5120c080ef\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"9a47ab79-dbb3-46e2-ad27-3a5120c080ef\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"horencho@gmail.com\"} {\"correlation_id\":\"9a47ab79-dbb3-46e2-ad27-3a5120c080ef\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":612340,\"team_id\":2,\"email\":\"horen.kirazyan@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"9a47ab79-dbb3-46e2-ad27-3a5120c080ef\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [Hubspot] Pagination completed {\"team_id\":2,\"endpoint\":\"https://api.hubapi.com/crm/v3/objects/contact/search\",\"total_requests\":1,\"total_records_fetched\":0,\"total_elapsed_seconds\":0.24,\"average_seconds_per_request\":0.24} {\"correlation_id\":\"9a47ab79-dbb3-46e2-ad27-3a5120c080ef\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":612340,\"participants_processed\":4,\"exact_matches\":1,\"domain_matches\":0,\"best_match_found\":true} {\"correlation_id\":\"9a47ab79-dbb3-46e2-ad27-3a5120c080ef\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [ EsUpdateProcessManager ] Finished updating entities in ES {\"worker\":\"\",\"peak_memory\":\"99.73 MB\",\"elapsed_seconds\":0.45,\"update_target\":\"activities\",\"should_iterate_again\":false} {\"correlation_id\":\"38935ae1-fcb3-437e-9c9e-1b7415a0b7e1\",\"trace_id\":\"325a2f9b-9c66-4223-ac02-1a43723f4315\"}\n[2026-05-11 11:06:16] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612340} {\"correlation_id\":\"9a47ab79-dbb3-46e2-ad27-3a5120c080ef\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612340,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"9a47ab79-dbb3-46e2-ad27-3a5120c080ef\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":612340,\"remote_search\":true,\"lead_id\":null,\"contact_id\":4491,\"account_id\":243,\"opportunity_id\":276,\"stage_id\":36} {\"correlation_id\":\"9a47ab79-dbb3-46e2-ad27-3a5120c080ef\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":612339,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":4491,\"account_id\":243,\"opportunity_id\":276,\"stage_id\":36}} {\"correlation_id\":\"eae16400-35f9-4885-b5fc-edb8872350d4\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612339} {\"correlation_id\":\"eae16400-35f9-4885-b5fc-edb8872350d4\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612339,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"eae16400-35f9-4885-b5fc-edb8872350d4\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":612339,\"participants\":[{\"id\":999514,\"user_id\":null,\"contact_id\":4491,\"lead_id\":null},{\"id\":999515,\"user_id\":206,\"contact_id\":null,\"lead_id\":null},{\"id\":999540,\"user_id\":null,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"eae16400-35f9-4885-b5fc-edb8872350d4\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"eae16400-35f9-4885-b5fc-edb8872350d4\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"eae16400-35f9-4885-b5fc-edb8872350d4\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"eae16400-35f9-4885-b5fc-edb8872350d4\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"eae16400-35f9-4885-b5fc-edb8872350d4\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"horencho@gmail.com\"} {\"correlation_id\":\"eae16400-35f9-4885-b5fc-edb8872350d4\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":612339,\"team_id\":2,\"email\":\"horen.kirazyan@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"eae16400-35f9-4885-b5fc-edb8872350d4\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":612339,\"participants_processed\":3,\"exact_matches\":1,\"domain_matches\":0,\"best_match_found\":true} {\"correlation_id\":\"eae16400-35f9-4885-b5fc-edb8872350d4\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612339} {\"correlation_id\":\"eae16400-35f9-4885-b5fc-edb8872350d4\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612339,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"eae16400-35f9-4885-b5fc-edb8872350d4\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":612339,\"remote_search\":true,\"lead_id\":null,\"contact_id\":4491,\"account_id\":243,\"opportunity_id\":276,\"stage_id\":36} {\"correlation_id\":\"eae16400-35f9-4885-b5fc-edb8872350d4\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":612336,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":4491,\"account_id\":243,\"opportunity_id\":276,\"stage_id\":36}} {\"correlation_id\":\"1028998b-3369-4f2f-b7cb-e4ef79db71a5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612336} {\"correlation_id\":\"1028998b-3369-4f2f-b7cb-e4ef79db71a5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612336,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"1028998b-3369-4f2f-b7cb-e4ef79db71a5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":612336,\"participants\":[{\"id\":999508,\"user_id\":null,\"contact_id\":4491,\"lead_id\":null},{\"id\":999509,\"user_id\":206,\"contact_id\":null,\"lead_id\":null},{\"id\":999512,\"user_id\":null,\"contact_id\":null,\"lead_id\":null},{\"id\":999513,\"user_id\":null,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"1028998b-3369-4f2f-b7cb-e4ef79db71a5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"1028998b-3369-4f2f-b7cb-e4ef79db71a5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"1028998b-3369-4f2f-b7cb-e4ef79db71a5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"1028998b-3369-4f2f-b7cb-e4ef79db71a5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"1028998b-3369-4f2f-b7cb-e4ef79db71a5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"horencho@gmail.com\"} {\"correlation_id\":\"1028998b-3369-4f2f-b7cb-e4ef79db71a5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":612336,\"team_id\":2,\"email\":\"horen.kirazyan@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"1028998b-3369-4f2f-b7cb-e4ef79db71a5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":612336,\"participants_processed\":4,\"exact_matches\":1,\"domain_matches\":0,\"best_match_found\":true} {\"correlation_id\":\"1028998b-3369-4f2f-b7cb-e4ef79db71a5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612336} {\"correlation_id\":\"1028998b-3369-4f2f-b7cb-e4ef79db71a5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612336,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"1028998b-3369-4f2f-b7cb-e4ef79db71a5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":612336,\"remote_search\":true,\"lead_id\":null,\"contact_id\":4491,\"account_id\":243,\"opportunity_id\":276,\"stage_id\":36} {\"correlation_id\":\"1028998b-3369-4f2f-b7cb-e4ef79db71a5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":612183,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":4487,\"account_id\":244,\"opportunity_id\":299,\"stage_id\":36}} {\"correlation_id\":\"135ad495-9ac2-4ae2-9428-66f40f3a4731\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612183} {\"correlation_id\":\"135ad495-9ac2-4ae2-9428-66f40f3a4731\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612183,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"135ad495-9ac2-4ae2-9428-66f40f3a4731\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":612183,\"participants\":[{\"id\":999227,\"user_id\":null,\"contact_id\":4487,\"lead_id\":null},{\"id\":999228,\"user_id\":261,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"135ad495-9ac2-4ae2-9428-66f40f3a4731\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"135ad495-9ac2-4ae2-9428-66f40f3a4731\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"135ad495-9ac2-4ae2-9428-66f40f3a4731\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"135ad495-9ac2-4ae2-9428-66f40f3a4731\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"135ad495-9ac2-4ae2-9428-66f40f3a4731\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: ProspectCache - Searching DB for opportunity by owner {\"account_id\":244,\"contact_id\":4487,\"owner_id\":261} {\"correlation_id\":\"135ad495-9ac2-4ae2-9428-66f40f3a4731\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: ProspectCache - Opportunity DB search results {\"account_id\":244,\"contact_id\":4487,\"opportunity_id\":299} {\"correlation_id\":\"135ad495-9ac2-4ae2-9428-66f40f3a4731\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"robinson@crusoe.com\"} {\"correlation_id\":\"135ad495-9ac2-4ae2-9428-66f40f3a4731\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":612183,\"team_id\":2,\"email\":\"adelina.petrova@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"135ad495-9ac2-4ae2-9428-66f40f3a4731\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":612183,\"participants_processed\":2,\"exact_matches\":1,\"domain_matches\":0,\"best_match_found\":true} {\"correlation_id\":\"135ad495-9ac2-4ae2-9428-66f40f3a4731\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612183} {\"correlation_id\":\"135ad495-9ac2-4ae2-9428-66f40f3a4731\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612183,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"135ad495-9ac2-4ae2-9428-66f40f3a4731\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":612183,\"remote_search\":true,\"lead_id\":null,\"contact_id\":4487,\"account_id\":244,\"opportunity_id\":299,\"stage_id\":36} {\"correlation_id\":\"135ad495-9ac2-4ae2-9428-66f40f3a4731\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":612182,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":4487,\"account_id\":244,\"opportunity_id\":299,\"stage_id\":36}} {\"correlation_id\":\"9a2d51b0-64bc-41ea-86b1-8f3bcbb65a89\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612182} {\"correlation_id\":\"9a2d51b0-64bc-41ea-86b1-8f3bcbb65a89\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612182,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"9a2d51b0-64bc-41ea-86b1-8f3bcbb65a89\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":612182,\"participants\":[{\"id\":999225,\"user_id\":null,\"contact_id\":4487,\"lead_id\":null},{\"id\":999226,\"user_id\":261,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"9a2d51b0-64bc-41ea-86b1-8f3bcbb65a89\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"mailbox:batch:process\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.727} {\"correlation_id\":\"dceccba8-9e9c-476e-993d-809067884ebe\",\"trace_id\":\"66f8bba8-1712-464b-b25a-418f16736be2\"}\n[2026-05-11 11:06:17] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"9a2d51b0-64bc-41ea-86b1-8f3bcbb65a89\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"9a2d51b0-64bc-41ea-86b1-8f3bcbb65a89\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"9a2d51b0-64bc-41ea-86b1-8f3bcbb65a89\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"9a2d51b0-64bc-41ea-86b1-8f3bcbb65a89\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"robinson@crusoe.com\"} {\"correlation_id\":\"9a2d51b0-64bc-41ea-86b1-8f3bcbb65a89\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":612182,\"team_id\":2,\"email\":\"adelina.petrova@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"9a2d51b0-64bc-41ea-86b1-8f3bcbb65a89\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":612182,\"participants_processed\":2,\"exact_matches\":1,\"domain_matches\":0,\"best_match_found\":true} {\"correlation_id\":\"9a2d51b0-64bc-41ea-86b1-8f3bcbb65a89\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612182} {\"correlation_id\":\"9a2d51b0-64bc-41ea-86b1-8f3bcbb65a89\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612182,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"9a2d51b0-64bc-41ea-86b1-8f3bcbb65a89\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [EmailSchedule] STARTING batch process {\"host\":\"docker_lamp_1\"} {\"correlation_id\":\"dceccba8-9e9c-476e-993d-809067884ebe\",\"trace_id\":\"66f8bba8-1712-464b-b25a-418f16736be2\"}\n[2026-05-11 11:06:17] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":612182,\"remote_search\":true,\"lead_id\":null,\"contact_id\":4487,\"account_id\":244,\"opportunity_id\":299,\"stage_id\":36} {\"correlation_id\":\"9a2d51b0-64bc-41ea-86b1-8f3bcbb65a89\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [EmailSchedule] FINISHED batch process {\"host\":\"docker_lamp_1\",\"processed\":0} {\"correlation_id\":\"dceccba8-9e9c-476e-993d-809067884ebe\",\"trace_id\":\"66f8bba8-1712-464b-b25a-418f16736be2\"}\n[2026-05-11 11:06:17] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage for command {\"command\":\"mailbox:batch:process\",\"memoryBeforeCommandInMb\":60.0,\"memoryAfterCommandInMB\":60.0,\"memoryPeakBeforeCommandInMb\":99.727,\"memoryPeakAfterCommandInMB\":99.727} {\"correlation_id\":\"dceccba8-9e9c-476e-993d-809067884ebe\",\"trace_id\":\"66f8bba8-1712-464b-b25a-418f16736be2\"}\n[2026-05-11 11:06:17] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":612181,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":4487,\"account_id\":244,\"opportunity_id\":299,\"stage_id\":36}} {\"correlation_id\":\"8ff40f83-337f-432a-b950-e35779bb59bf\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612181} {\"correlation_id\":\"8ff40f83-337f-432a-b950-e35779bb59bf\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612181,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"8ff40f83-337f-432a-b950-e35779bb59bf\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":612181,\"participants\":[{\"id\":999223,\"user_id\":null,\"contact_id\":4487,\"lead_id\":null},{\"id\":999224,\"user_id\":261,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"8ff40f83-337f-432a-b950-e35779bb59bf\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"8ff40f83-337f-432a-b950-e35779bb59bf\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"8ff40f83-337f-432a-b950-e35779bb59bf\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"8ff40f83-337f-432a-b950-e35779bb59bf\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"8ff40f83-337f-432a-b950-e35779bb59bf\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"robinson@crusoe.com\"} {\"correlation_id\":\"8ff40f83-337f-432a-b950-e35779bb59bf\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":612181,\"team_id\":2,\"email\":\"adelina.petrova@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"8ff40f83-337f-432a-b950-e35779bb59bf\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":612181,\"participants_processed\":2,\"exact_matches\":1,\"domain_matches\":0,\"best_match_found\":true} {\"correlation_id\":\"8ff40f83-337f-432a-b950-e35779bb59bf\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612181} {\"correlation_id\":\"8ff40f83-337f-432a-b950-e35779bb59bf\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612181,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"8ff40f83-337f-432a-b950-e35779bb59bf\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":612181,\"remote_search\":true,\"lead_id\":null,\"contact_id\":4487,\"account_id\":244,\"opportunity_id\":299,\"stage_id\":36} {\"correlation_id\":\"8ff40f83-337f-432a-b950-e35779bb59bf\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":612180,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":4487,\"account_id\":244,\"opportunity_id\":299,\"stage_id\":36}} {\"correlation_id\":\"03f05b91-f803-45c9-b4bf-6a5f8ddf4a2c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612180} {\"correlation_id\":\"03f05b91-f803-45c9-b4bf-6a5f8ddf4a2c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612180,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"03f05b91-f803-45c9-b4bf-6a5f8ddf4a2c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":612180,\"participants\":[{\"id\":999221,\"user_id\":null,\"contact_id\":4487,\"lead_id\":null},{\"id\":999222,\"user_id\":261,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"03f05b91-f803-45c9-b4bf-6a5f8ddf4a2c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"03f05b91-f803-45c9-b4bf-6a5f8ddf4a2c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"03f05b91-f803-45c9-b4bf-6a5f8ddf4a2c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"03f05b91-f803-45c9-b4bf-6a5f8ddf4a2c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"03f05b91-f803-45c9-b4bf-6a5f8ddf4a2c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"robinson@crusoe.com\"} {\"correlation_id\":\"03f05b91-f803-45c9-b4bf-6a5f8ddf4a2c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":612180,\"team_id\":2,\"email\":\"adelina.petrova@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"03f05b91-f803-45c9-b4bf-6a5f8ddf4a2c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":612180,\"participants_processed\":2,\"exact_matches\":1,\"domain_matches\":0,\"best_match_found\":true} {\"correlation_id\":\"03f05b91-f803-45c9-b4bf-6a5f8ddf4a2c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612180} {\"correlation_id\":\"03f05b91-f803-45c9-b4bf-6a5f8ddf4a2c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612180,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"03f05b91-f803-45c9-b4bf-6a5f8ddf4a2c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":612180,\"remote_search\":true,\"lead_id\":null,\"contact_id\":4487,\"account_id\":244,\"opportunity_id\":299,\"stage_id\":36} {\"correlation_id\":\"03f05b91-f803-45c9-b4bf-6a5f8ddf4a2c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":611455,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89}} {\"correlation_id\":\"299c67e4-7026-46ee-b10d-d4e48fb55a7c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":611455} {\"correlation_id\":\"299c67e4-7026-46ee-b10d-d4e48fb55a7c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":611455,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"299c67e4-7026-46ee-b10d-d4e48fb55a7c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":611455,\"participants\":[{\"id\":997961,\"user_id\":null,\"contact_id\":null,\"lead_id\":null},{\"id\":997962,\"user_id\":1460,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"299c67e4-7026-46ee-b10d-d4e48fb55a7c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"299c67e4-7026-46ee-b10d-d4e48fb55a7c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"299c67e4-7026-46ee-b10d-d4e48fb55a7c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"299c67e4-7026-46ee-b10d-d4e48fb55a7c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"299c67e4-7026-46ee-b10d-d4e48fb55a7c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [Prospect match] Cache miss, calling the API {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"299c67e4-7026-46ee-b10d-d4e48fb55a7c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [Hubspot] Failed to fetch contact {\"email\":\"support@staging.jiminny.com\",\"reason\":\"[404] Client error: `GET https://api.hubapi.com/crm/v3/objects/contacts/support%40staging.jiminny.com?properties=email%2Cfirstname%2Clastname%2Ccountry%2Cphone%2Cmobilephone%2Cjobtitle%2Chubspot_owner_id%2Cassociatedcompanyid%2Cphoto&archived=0&idProperty=email` resulted in a `404 Not Found` response\"} {\"correlation_id\":\"299c67e4-7026-46ee-b10d-d4e48fb55a7c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [Prospect match] API returned empty result, caching the miss with empty prospect data {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"299c67e4-7026-46ee-b10d-d4e48fb55a7c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [Hubspot] Pagination completed {\"team_id\":2,\"endpoint\":\"https://api.hubapi.com/crm/v3/objects/contact/search\",\"total_requests\":1,\"total_records_fetched\":0,\"total_elapsed_seconds\":0.24,\"average_seconds_per_request\":0.24} {\"correlation_id\":\"299c67e4-7026-46ee-b10d-d4e48fb55a7c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [Prospect match] Resolved company domain from email {\"email\":\"support@staging.jiminny.com\",\"domain\":\"jiminny.com\"} {\"correlation_id\":\"299c67e4-7026-46ee-b10d-d4e48fb55a7c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [Prospect match] Cache miss {\"identifier_type\":\"domain\",\"identifier\":\"jiminny.com\",\"crm\":\"hubspot\"} {\"correlation_id\":\"299c67e4-7026-46ee-b10d-d4e48fb55a7c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [Prospect match] Cache miss, calling the API {\"identifier_type\":\"domain\",\"identifier\":\"jiminny.com\"} {\"correlation_id\":\"299c67e4-7026-46ee-b10d-d4e48fb55a7c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:19] local.INFO: [HubSpot] importAccount {\"crm_provider_id\":\"749766179\",\"config_id\":2} {\"correlation_id\":\"299c67e4-7026-46ee-b10d-d4e48fb55a7c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:19] local.INFO: [HubSpot] CRM Search requested {\"request\":{\"filterGroups\":[{\"filters\":[{\"propertyName\":\"associations.company\",\"operator\":\"EQ\",\"value\":\"749766179\"},{\"propertyName\":\"dealstage\",\"operator\":\"NOT_IN\",\"values\":[\"closedwon\",\"4040964\",\"59247967\"]},{\"propertyName\":\"dealstage\",\"operator\":\"NOT_IN\",\"values\":[\"closedlost\",\"4040965\",\"59247968\"]}]}],\"sorts\":[{\"propertyName\":\"modifieddate\",\"direction\":\"DESCENDING\"}],\"properties\":[\"dealname\",\"amount\",\"hubspot_owner_id\",\"pipeline\",\"dealstage\",\"closedate\",\"deal_currency_code\"],\"limit\":200}} {\"correlation_id\":\"299c67e4-7026-46ee-b10d-d4e48fb55a7c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:19] local.INFO: [Hubspot] Pagination completed {\"team_id\":2,\"endpoint\":\"https://api.hubapi.com/crm/v3/objects/deals/search\",\"total_requests\":1,\"total_records_fetched\":10,\"total_elapsed_seconds\":0.34,\"average_seconds_per_request\":0.34} {\"correlation_id\":\"299c67e4-7026-46ee-b10d-d4e48fb55a7c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}","depth":4,"on_screen":true,"value":"[2026-05-11 11:06:08] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":612562,\"team_id\":2,\"email\":\"horen.kirazyan@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"c1949c5e-c0f4-431e-ba26-59d49408d871\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:08] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":612562,\"participants_processed\":2,\"exact_matches\":0,\"domain_matches\":1,\"best_match_found\":true} {\"correlation_id\":\"c1949c5e-c0f4-431e-ba26-59d49408d871\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:08] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612562} {\"correlation_id\":\"c1949c5e-c0f4-431e-ba26-59d49408d871\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:08] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612562,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"c1949c5e-c0f4-431e-ba26-59d49408d871\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:08] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":612562,\"remote_search\":true,\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89} {\"correlation_id\":\"c1949c5e-c0f4-431e-ba26-59d49408d871\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:09] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":612561,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89}} {\"correlation_id\":\"b715f531-04da-410f-be31-055aa1c13754\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:09] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612561} {\"correlation_id\":\"b715f531-04da-410f-be31-055aa1c13754\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:09] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612561,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"b715f531-04da-410f-be31-055aa1c13754\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:09] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":612561,\"participants\":[{\"id\":999780,\"user_id\":206,\"contact_id\":null,\"lead_id\":null},{\"id\":999781,\"user_id\":null,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"b715f531-04da-410f-be31-055aa1c13754\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:09] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"b715f531-04da-410f-be31-055aa1c13754\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:09] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"b715f531-04da-410f-be31-055aa1c13754\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:09] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"b715f531-04da-410f-be31-055aa1c13754\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:09] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"b715f531-04da-410f-be31-055aa1c13754\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:09] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":612561,\"team_id\":2,\"email\":\"horen.kirazyan@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"b715f531-04da-410f-be31-055aa1c13754\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:09] local.INFO: [Prospect match] Cache miss, calling the API {\"identifier_type\":\"email\",\"identifier\":\"447700174614.447782589921.OeREojLVnk@txt.staging.jiminny.com\"} {\"correlation_id\":\"b715f531-04da-410f-be31-055aa1c13754\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:10] local.INFO: [Hubspot] Failed to fetch contact {\"email\":\"447700174614.447782589921.OeREojLVnk@txt.staging.jiminny.com\",\"reason\":\"[404] Client error: `GET https://api.hubapi.com/crm/v3/objects/contacts/447700174614.447782589921.OeREojLVnk%40txt.staging.jiminny.com?properties=email%2Cfirstname%2Clastname%2Ccountry%2Cphone%2Cmobilephone%2Cjobtitle%2Chubspot_owner_id%2Cassociatedcompanyid%2Cphoto&archived=0&idProperty=email` resulted in a `404 Not Found` response\"} {\"correlation_id\":\"b715f531-04da-410f-be31-055aa1c13754\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:10] local.INFO: [Prospect match] API returned empty result, caching the miss with empty prospect data {\"identifier_type\":\"email\",\"identifier\":\"447700174614.447782589921.OeREojLVnk@txt.staging.jiminny.com\"} {\"correlation_id\":\"b715f531-04da-410f-be31-055aa1c13754\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:10] local.INFO: [Prospect match] Resolved company domain from email {\"email\":\"447700174614.447782589921.OeREojLVnk@txt.staging.jiminny.com\",\"domain\":\"jiminny.com\"} {\"correlation_id\":\"b715f531-04da-410f-be31-055aa1c13754\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:10] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":612561,\"participants_processed\":2,\"exact_matches\":0,\"domain_matches\":1,\"best_match_found\":true} {\"correlation_id\":\"b715f531-04da-410f-be31-055aa1c13754\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:10] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612561} {\"correlation_id\":\"b715f531-04da-410f-be31-055aa1c13754\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:10] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612561,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"b715f531-04da-410f-be31-055aa1c13754\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:10] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":612561,\"remote_search\":true,\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89} {\"correlation_id\":\"b715f531-04da-410f-be31-055aa1c13754\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:10] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":612560,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89}} {\"correlation_id\":\"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:10] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612560} {\"correlation_id\":\"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:10] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612560,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:10] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":612560,\"participants\":[{\"id\":999778,\"user_id\":null,\"contact_id\":null,\"lead_id\":null},{\"id\":999779,\"user_id\":206,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"447782589921@txt.staging.jiminny.com\"} {\"correlation_id\":\"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [Prospect match] cached empty result - no API calls, try next matching method {\"identifier_type\":\"email\",\"identifier\":\"447782589921@txt.staging.jiminny.com\"} {\"correlation_id\":\"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [Prospect match] Resolved company domain from email {\"email\":\"447782589921@txt.staging.jiminny.com\",\"domain\":\"jiminny.com\"} {\"correlation_id\":\"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":612560,\"team_id\":2,\"email\":\"horen.kirazyan@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":612560,\"participants_processed\":2,\"exact_matches\":0,\"domain_matches\":1,\"best_match_found\":true} {\"correlation_id\":\"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612560} {\"correlation_id\":\"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [ EsUpdateProcessManager ] Finished updating entities in ES {\"worker\":\"\",\"peak_memory\":\"99.73 MB\",\"elapsed_seconds\":0.74,\"update_target\":\"activities\",\"should_iterate_again\":false} {\"correlation_id\":\"38935ae1-fcb3-437e-9c9e-1b7415a0b7e1\",\"trace_id\":\"325a2f9b-9c66-4223-ac02-1a43723f4315\"}\n[2026-05-11 11:06:11] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612560,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":612560,\"remote_search\":true,\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89} {\"correlation_id\":\"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":612559,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":97,\"account_id\":69,\"opportunity_id\":5011,\"stage_id\":34}} {\"correlation_id\":\"4d59a59c-38a5-4a1d-9379-6ceaf67b747e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612559} {\"correlation_id\":\"4d59a59c-38a5-4a1d-9379-6ceaf67b747e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612559,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"4d59a59c-38a5-4a1d-9379-6ceaf67b747e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":612559,\"participants\":[{\"id\":999776,\"user_id\":206,\"contact_id\":null,\"lead_id\":null},{\"id\":999777,\"user_id\":null,\"contact_id\":97,\"lead_id\":null}]} {\"correlation_id\":\"4d59a59c-38a5-4a1d-9379-6ceaf67b747e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"4d59a59c-38a5-4a1d-9379-6ceaf67b747e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"4d59a59c-38a5-4a1d-9379-6ceaf67b747e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"4d59a59c-38a5-4a1d-9379-6ceaf67b747e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"4d59a59c-38a5-4a1d-9379-6ceaf67b747e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":612559,\"team_id\":2,\"email\":\"horen.kirazyan@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"4d59a59c-38a5-4a1d-9379-6ceaf67b747e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: ProspectCache - Searching DB for opportunity by owner {\"account_id\":69,\"contact_id\":97,\"owner_id\":206} {\"correlation_id\":\"4d59a59c-38a5-4a1d-9379-6ceaf67b747e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: ProspectCache - Fallback DB opportunity search {\"account_id\":69,\"contact_id\":97} {\"correlation_id\":\"4d59a59c-38a5-4a1d-9379-6ceaf67b747e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: ProspectCache - Opportunity DB search results {\"account_id\":69,\"contact_id\":97,\"opportunity_id\":5011} {\"correlation_id\":\"4d59a59c-38a5-4a1d-9379-6ceaf67b747e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"robinsoncrusoe@test.com\"} {\"correlation_id\":\"4d59a59c-38a5-4a1d-9379-6ceaf67b747e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":612559,\"participants_processed\":2,\"exact_matches\":1,\"domain_matches\":0,\"best_match_found\":true} {\"correlation_id\":\"4d59a59c-38a5-4a1d-9379-6ceaf67b747e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612559} {\"correlation_id\":\"4d59a59c-38a5-4a1d-9379-6ceaf67b747e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612559,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"4d59a59c-38a5-4a1d-9379-6ceaf67b747e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":612559,\"remote_search\":true,\"lead_id\":null,\"contact_id\":97,\"account_id\":69,\"opportunity_id\":5011,\"stage_id\":34} {\"correlation_id\":\"4d59a59c-38a5-4a1d-9379-6ceaf67b747e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:12] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":612558,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":97,\"account_id\":69,\"opportunity_id\":5011,\"stage_id\":34}} {\"correlation_id\":\"a105800d-d2f7-4772-a534-7791e262461c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:12] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612558} {\"correlation_id\":\"a105800d-d2f7-4772-a534-7791e262461c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:12] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612558,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"a105800d-d2f7-4772-a534-7791e262461c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:12] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":612558,\"participants\":[{\"id\":999774,\"user_id\":206,\"contact_id\":null,\"lead_id\":null},{\"id\":999775,\"user_id\":null,\"contact_id\":97,\"lead_id\":null}]} {\"correlation_id\":\"a105800d-d2f7-4772-a534-7791e262461c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:12] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"a105800d-d2f7-4772-a534-7791e262461c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:12] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"a105800d-d2f7-4772-a534-7791e262461c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:12] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"a105800d-d2f7-4772-a534-7791e262461c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:12] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"a105800d-d2f7-4772-a534-7791e262461c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:12] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":612558,\"team_id\":2,\"email\":\"horen.kirazyan@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"a105800d-d2f7-4772-a534-7791e262461c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:12] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"robinsoncrusoe@test.com\"} {\"correlation_id\":\"a105800d-d2f7-4772-a534-7791e262461c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:12] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":612558,\"participants_processed\":2,\"exact_matches\":1,\"domain_matches\":0,\"best_match_found\":true} {\"correlation_id\":\"a105800d-d2f7-4772-a534-7791e262461c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:12] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612558} {\"correlation_id\":\"a105800d-d2f7-4772-a534-7791e262461c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:12] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612558,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"a105800d-d2f7-4772-a534-7791e262461c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:12] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":612558,\"remote_search\":true,\"lead_id\":null,\"contact_id\":97,\"account_id\":69,\"opportunity_id\":5011,\"stage_id\":34} {\"correlation_id\":\"a105800d-d2f7-4772-a534-7791e262461c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:13] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":612557,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":97,\"account_id\":69,\"opportunity_id\":5011,\"stage_id\":34}} {\"correlation_id\":\"3ec57437-5796-4e44-8bf0-aaec149ac813\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:13] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612557} {\"correlation_id\":\"3ec57437-5796-4e44-8bf0-aaec149ac813\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:13] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612557,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"3ec57437-5796-4e44-8bf0-aaec149ac813\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:13] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":612557,\"participants\":[{\"id\":999772,\"user_id\":206,\"contact_id\":null,\"lead_id\":null},{\"id\":999773,\"user_id\":null,\"contact_id\":97,\"lead_id\":null}]} {\"correlation_id\":\"3ec57437-5796-4e44-8bf0-aaec149ac813\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:13] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"mailbox:skip-lists:refresh\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.727} {\"correlation_id\":\"5203040f-23be-43e6-8410-ba49ee1b38b4\",\"trace_id\":\"e787953d-9b78-4b63-a987-4f93f6892984\"}\n[2026-05-11 11:06:13] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"3ec57437-5796-4e44-8bf0-aaec149ac813\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:13] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"3ec57437-5796-4e44-8bf0-aaec149ac813\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:13] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"3ec57437-5796-4e44-8bf0-aaec149ac813\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:13] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"3ec57437-5796-4e44-8bf0-aaec149ac813\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:13] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":612557,\"team_id\":2,\"email\":\"horen.kirazyan@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"3ec57437-5796-4e44-8bf0-aaec149ac813\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:13] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"robinsoncrusoe@test.com\"} {\"correlation_id\":\"3ec57437-5796-4e44-8bf0-aaec149ac813\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:13] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":612557,\"participants_processed\":2,\"exact_matches\":1,\"domain_matches\":0,\"best_match_found\":true} {\"correlation_id\":\"3ec57437-5796-4e44-8bf0-aaec149ac813\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:13] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612557} {\"correlation_id\":\"3ec57437-5796-4e44-8bf0-aaec149ac813\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:13] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612557,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"3ec57437-5796-4e44-8bf0-aaec149ac813\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:13] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":612557,\"remote_search\":true,\"lead_id\":null,\"contact_id\":97,\"account_id\":69,\"opportunity_id\":5011,\"stage_id\":34} {\"correlation_id\":\"3ec57437-5796-4e44-8bf0-aaec149ac813\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:13] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage for command {\"command\":\"mailbox:skip-lists:refresh\",\"memoryBeforeCommandInMb\":60.0,\"memoryAfterCommandInMB\":60.0,\"memoryPeakBeforeCommandInMb\":99.727,\"memoryPeakAfterCommandInMB\":99.727} {\"correlation_id\":\"5203040f-23be-43e6-8410-ba49ee1b38b4\",\"trace_id\":\"e787953d-9b78-4b63-a987-4f93f6892984\"}\n[2026-05-11 11:06:13] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":612556,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":97,\"account_id\":69,\"opportunity_id\":5011,\"stage_id\":34}} {\"correlation_id\":\"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:13] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612556} {\"correlation_id\":\"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:13] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612556,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:13] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":612556,\"participants\":[{\"id\":999770,\"user_id\":206,\"contact_id\":null,\"lead_id\":null},{\"id\":999771,\"user_id\":null,\"contact_id\":97,\"lead_id\":null}]} {\"correlation_id\":\"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:13] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:13] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:13] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:13] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":612556,\"team_id\":2,\"email\":\"horen.kirazyan@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"robinsoncrusoe@test.com\"} {\"correlation_id\":\"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":612556,\"participants_processed\":2,\"exact_matches\":1,\"domain_matches\":0,\"best_match_found\":true} {\"correlation_id\":\"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612556} {\"correlation_id\":\"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612556,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":612556,\"remote_search\":true,\"lead_id\":null,\"contact_id\":97,\"account_id\":69,\"opportunity_id\":5011,\"stage_id\":34} {\"correlation_id\":\"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":612555,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":97,\"account_id\":69,\"opportunity_id\":5011,\"stage_id\":34}} {\"correlation_id\":\"3e9d1a70-5491-44ae-8f28-5cb9b60e1859\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612555} {\"correlation_id\":\"3e9d1a70-5491-44ae-8f28-5cb9b60e1859\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612555,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"3e9d1a70-5491-44ae-8f28-5cb9b60e1859\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":612555,\"participants\":[{\"id\":999768,\"user_id\":206,\"contact_id\":null,\"lead_id\":null},{\"id\":999769,\"user_id\":null,\"contact_id\":97,\"lead_id\":null}]} {\"correlation_id\":\"3e9d1a70-5491-44ae-8f28-5cb9b60e1859\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"3e9d1a70-5491-44ae-8f28-5cb9b60e1859\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"3e9d1a70-5491-44ae-8f28-5cb9b60e1859\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"3e9d1a70-5491-44ae-8f28-5cb9b60e1859\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"3e9d1a70-5491-44ae-8f28-5cb9b60e1859\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":612555,\"team_id\":2,\"email\":\"horen.kirazyan@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"3e9d1a70-5491-44ae-8f28-5cb9b60e1859\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"robinsoncrusoe@test.com\"} {\"correlation_id\":\"3e9d1a70-5491-44ae-8f28-5cb9b60e1859\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":612555,\"participants_processed\":2,\"exact_matches\":1,\"domain_matches\":0,\"best_match_found\":true} {\"correlation_id\":\"3e9d1a70-5491-44ae-8f28-5cb9b60e1859\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612555} {\"correlation_id\":\"3e9d1a70-5491-44ae-8f28-5cb9b60e1859\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612555,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"3e9d1a70-5491-44ae-8f28-5cb9b60e1859\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":612555,\"remote_search\":true,\"lead_id\":null,\"contact_id\":97,\"account_id\":69,\"opportunity_id\":5011,\"stage_id\":34} {\"correlation_id\":\"3e9d1a70-5491-44ae-8f28-5cb9b60e1859\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":612554,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":97,\"account_id\":69,\"opportunity_id\":5011,\"stage_id\":34}} {\"correlation_id\":\"7101e979-3a7e-4b2c-bf04-dc6a24ba1356\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612554} {\"correlation_id\":\"7101e979-3a7e-4b2c-bf04-dc6a24ba1356\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612554,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"7101e979-3a7e-4b2c-bf04-dc6a24ba1356\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":612554,\"participants\":[{\"id\":999766,\"user_id\":206,\"contact_id\":null,\"lead_id\":null},{\"id\":999767,\"user_id\":null,\"contact_id\":97,\"lead_id\":null}]} {\"correlation_id\":\"7101e979-3a7e-4b2c-bf04-dc6a24ba1356\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"7101e979-3a7e-4b2c-bf04-dc6a24ba1356\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"7101e979-3a7e-4b2c-bf04-dc6a24ba1356\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"7101e979-3a7e-4b2c-bf04-dc6a24ba1356\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"7101e979-3a7e-4b2c-bf04-dc6a24ba1356\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":612554,\"team_id\":2,\"email\":\"horen.kirazyan@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"7101e979-3a7e-4b2c-bf04-dc6a24ba1356\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"robinsoncrusoe@test.com\"} {\"correlation_id\":\"7101e979-3a7e-4b2c-bf04-dc6a24ba1356\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":612554,\"participants_processed\":2,\"exact_matches\":1,\"domain_matches\":0,\"best_match_found\":true} {\"correlation_id\":\"7101e979-3a7e-4b2c-bf04-dc6a24ba1356\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612554} {\"correlation_id\":\"7101e979-3a7e-4b2c-bf04-dc6a24ba1356\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612554,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"7101e979-3a7e-4b2c-bf04-dc6a24ba1356\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":612554,\"remote_search\":true,\"lead_id\":null,\"contact_id\":97,\"account_id\":69,\"opportunity_id\":5011,\"stage_id\":34} {\"correlation_id\":\"7101e979-3a7e-4b2c-bf04-dc6a24ba1356\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":612553,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":97,\"account_id\":69,\"opportunity_id\":5011,\"stage_id\":34}} {\"correlation_id\":\"346dc229-757b-496c-aceb-d2ddb367c7d6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612553} {\"correlation_id\":\"346dc229-757b-496c-aceb-d2ddb367c7d6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612553,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"346dc229-757b-496c-aceb-d2ddb367c7d6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":612553,\"participants\":[{\"id\":999764,\"user_id\":206,\"contact_id\":null,\"lead_id\":null},{\"id\":999765,\"user_id\":null,\"contact_id\":97,\"lead_id\":null}]} {\"correlation_id\":\"346dc229-757b-496c-aceb-d2ddb367c7d6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"346dc229-757b-496c-aceb-d2ddb367c7d6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"346dc229-757b-496c-aceb-d2ddb367c7d6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"346dc229-757b-496c-aceb-d2ddb367c7d6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"346dc229-757b-496c-aceb-d2ddb367c7d6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":612553,\"team_id\":2,\"email\":\"horen.kirazyan@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"346dc229-757b-496c-aceb-d2ddb367c7d6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"robinsoncrusoe@test.com\"} {\"correlation_id\":\"346dc229-757b-496c-aceb-d2ddb367c7d6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":612553,\"participants_processed\":2,\"exact_matches\":1,\"domain_matches\":0,\"best_match_found\":true} {\"correlation_id\":\"346dc229-757b-496c-aceb-d2ddb367c7d6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612553} {\"correlation_id\":\"346dc229-757b-496c-aceb-d2ddb367c7d6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612553,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"346dc229-757b-496c-aceb-d2ddb367c7d6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":612553,\"remote_search\":true,\"lead_id\":null,\"contact_id\":97,\"account_id\":69,\"opportunity_id\":5011,\"stage_id\":34} {\"correlation_id\":\"346dc229-757b-496c-aceb-d2ddb367c7d6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":612552,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":97,\"account_id\":69,\"opportunity_id\":5011,\"stage_id\":34}} {\"correlation_id\":\"56ef8656-0bfa-4f11-a245-9254b8179555\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612552} {\"correlation_id\":\"56ef8656-0bfa-4f11-a245-9254b8179555\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612552,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"56ef8656-0bfa-4f11-a245-9254b8179555\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":612552,\"participants\":[{\"id\":999762,\"user_id\":206,\"contact_id\":null,\"lead_id\":null},{\"id\":999763,\"user_id\":null,\"contact_id\":97,\"lead_id\":null}]} {\"correlation_id\":\"56ef8656-0bfa-4f11-a245-9254b8179555\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"56ef8656-0bfa-4f11-a245-9254b8179555\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"56ef8656-0bfa-4f11-a245-9254b8179555\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"56ef8656-0bfa-4f11-a245-9254b8179555\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"56ef8656-0bfa-4f11-a245-9254b8179555\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":612552,\"team_id\":2,\"email\":\"horen.kirazyan@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"56ef8656-0bfa-4f11-a245-9254b8179555\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"robinsoncrusoe@test.com\"} {\"correlation_id\":\"56ef8656-0bfa-4f11-a245-9254b8179555\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":612552,\"participants_processed\":2,\"exact_matches\":1,\"domain_matches\":0,\"best_match_found\":true} {\"correlation_id\":\"56ef8656-0bfa-4f11-a245-9254b8179555\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612552} {\"correlation_id\":\"56ef8656-0bfa-4f11-a245-9254b8179555\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612552,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"56ef8656-0bfa-4f11-a245-9254b8179555\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":612552,\"remote_search\":true,\"lead_id\":null,\"contact_id\":97,\"account_id\":69,\"opportunity_id\":5011,\"stage_id\":34} {\"correlation_id\":\"56ef8656-0bfa-4f11-a245-9254b8179555\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":612551,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":97,\"account_id\":69,\"opportunity_id\":5011,\"stage_id\":34}} {\"correlation_id\":\"df5e35c3-b4ae-4eba-9108-cd30ac152335\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612551} {\"correlation_id\":\"df5e35c3-b4ae-4eba-9108-cd30ac152335\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612551,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"df5e35c3-b4ae-4eba-9108-cd30ac152335\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":612551,\"participants\":[{\"id\":999760,\"user_id\":206,\"contact_id\":null,\"lead_id\":null},{\"id\":999761,\"user_id\":null,\"contact_id\":97,\"lead_id\":null}]} {\"correlation_id\":\"df5e35c3-b4ae-4eba-9108-cd30ac152335\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"df5e35c3-b4ae-4eba-9108-cd30ac152335\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"df5e35c3-b4ae-4eba-9108-cd30ac152335\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"df5e35c3-b4ae-4eba-9108-cd30ac152335\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"df5e35c3-b4ae-4eba-9108-cd30ac152335\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":612551,\"team_id\":2,\"email\":\"horen.kirazyan@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"df5e35c3-b4ae-4eba-9108-cd30ac152335\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"robinsoncrusoe@test.com\"} {\"correlation_id\":\"df5e35c3-b4ae-4eba-9108-cd30ac152335\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":612551,\"participants_processed\":2,\"exact_matches\":1,\"domain_matches\":0,\"best_match_found\":true} {\"correlation_id\":\"df5e35c3-b4ae-4eba-9108-cd30ac152335\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612551} {\"correlation_id\":\"df5e35c3-b4ae-4eba-9108-cd30ac152335\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612551,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"df5e35c3-b4ae-4eba-9108-cd30ac152335\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":612551,\"remote_search\":true,\"lead_id\":null,\"contact_id\":97,\"account_id\":69,\"opportunity_id\":5011,\"stage_id\":34} {\"correlation_id\":\"df5e35c3-b4ae-4eba-9108-cd30ac152335\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":612550,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":97,\"account_id\":69,\"opportunity_id\":5011,\"stage_id\":34}} {\"correlation_id\":\"64768905-983e-4f4e-a368-901931486f34\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612550} {\"correlation_id\":\"64768905-983e-4f4e-a368-901931486f34\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612550,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"64768905-983e-4f4e-a368-901931486f34\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":612550,\"participants\":[{\"id\":999758,\"user_id\":206,\"contact_id\":null,\"lead_id\":null},{\"id\":999759,\"user_id\":null,\"contact_id\":97,\"lead_id\":null}]} {\"correlation_id\":\"64768905-983e-4f4e-a368-901931486f34\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"64768905-983e-4f4e-a368-901931486f34\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"64768905-983e-4f4e-a368-901931486f34\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"64768905-983e-4f4e-a368-901931486f34\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"64768905-983e-4f4e-a368-901931486f34\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":612550,\"team_id\":2,\"email\":\"horen.kirazyan@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"64768905-983e-4f4e-a368-901931486f34\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"robinsoncrusoe@test.com\"} {\"correlation_id\":\"64768905-983e-4f4e-a368-901931486f34\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":612550,\"participants_processed\":2,\"exact_matches\":1,\"domain_matches\":0,\"best_match_found\":true} {\"correlation_id\":\"64768905-983e-4f4e-a368-901931486f34\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612550} {\"correlation_id\":\"64768905-983e-4f4e-a368-901931486f34\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612550,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"64768905-983e-4f4e-a368-901931486f34\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":612550,\"remote_search\":true,\"lead_id\":null,\"contact_id\":97,\"account_id\":69,\"opportunity_id\":5011,\"stage_id\":34} {\"correlation_id\":\"64768905-983e-4f4e-a368-901931486f34\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":612549,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":97,\"account_id\":69,\"opportunity_id\":5011,\"stage_id\":34}} {\"correlation_id\":\"97c9609f-bef0-4e95-8301-9f5549d29c1e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612549} {\"correlation_id\":\"97c9609f-bef0-4e95-8301-9f5549d29c1e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612549,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"97c9609f-bef0-4e95-8301-9f5549d29c1e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":612549,\"participants\":[{\"id\":999756,\"user_id\":206,\"contact_id\":null,\"lead_id\":null},{\"id\":999757,\"user_id\":null,\"contact_id\":97,\"lead_id\":null}]} {\"correlation_id\":\"97c9609f-bef0-4e95-8301-9f5549d29c1e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"97c9609f-bef0-4e95-8301-9f5549d29c1e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"97c9609f-bef0-4e95-8301-9f5549d29c1e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"97c9609f-bef0-4e95-8301-9f5549d29c1e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"97c9609f-bef0-4e95-8301-9f5549d29c1e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":612549,\"team_id\":2,\"email\":\"horen.kirazyan@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"97c9609f-bef0-4e95-8301-9f5549d29c1e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"robinsoncrusoe@test.com\"} {\"correlation_id\":\"97c9609f-bef0-4e95-8301-9f5549d29c1e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":612549,\"participants_processed\":2,\"exact_matches\":1,\"domain_matches\":0,\"best_match_found\":true} {\"correlation_id\":\"97c9609f-bef0-4e95-8301-9f5549d29c1e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612549} {\"correlation_id\":\"97c9609f-bef0-4e95-8301-9f5549d29c1e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612549,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"97c9609f-bef0-4e95-8301-9f5549d29c1e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":612549,\"remote_search\":true,\"lead_id\":null,\"contact_id\":97,\"account_id\":69,\"opportunity_id\":5011,\"stage_id\":34} {\"correlation_id\":\"97c9609f-bef0-4e95-8301-9f5549d29c1e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":612365,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":4491,\"account_id\":243,\"opportunity_id\":276,\"stage_id\":36}} {\"correlation_id\":\"7761b4e0-c2d4-4805-8d1d-26728cbffb07\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612365} {\"correlation_id\":\"7761b4e0-c2d4-4805-8d1d-26728cbffb07\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612365,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"7761b4e0-c2d4-4805-8d1d-26728cbffb07\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":612365,\"participants\":[{\"id\":999563,\"user_id\":null,\"contact_id\":4491,\"lead_id\":null},{\"id\":999564,\"user_id\":206,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"7761b4e0-c2d4-4805-8d1d-26728cbffb07\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"7761b4e0-c2d4-4805-8d1d-26728cbffb07\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"7761b4e0-c2d4-4805-8d1d-26728cbffb07\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"7761b4e0-c2d4-4805-8d1d-26728cbffb07\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"7761b4e0-c2d4-4805-8d1d-26728cbffb07\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"horencho@gmail.com\"} {\"correlation_id\":\"7761b4e0-c2d4-4805-8d1d-26728cbffb07\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":612365,\"team_id\":2,\"email\":\"horen.kirazyan@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"7761b4e0-c2d4-4805-8d1d-26728cbffb07\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":612365,\"participants_processed\":2,\"exact_matches\":1,\"domain_matches\":0,\"best_match_found\":true} {\"correlation_id\":\"7761b4e0-c2d4-4805-8d1d-26728cbffb07\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612365} {\"correlation_id\":\"7761b4e0-c2d4-4805-8d1d-26728cbffb07\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612365,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"7761b4e0-c2d4-4805-8d1d-26728cbffb07\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":612365,\"remote_search\":true,\"lead_id\":null,\"contact_id\":4491,\"account_id\":243,\"opportunity_id\":276,\"stage_id\":36} {\"correlation_id\":\"7761b4e0-c2d4-4805-8d1d-26728cbffb07\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":612360,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":4491,\"account_id\":243,\"opportunity_id\":276,\"stage_id\":36}} {\"correlation_id\":\"b8b554cd-4830-4ac5-be75-595cc7eb6ca8\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612360} {\"correlation_id\":\"b8b554cd-4830-4ac5-be75-595cc7eb6ca8\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612360,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"b8b554cd-4830-4ac5-be75-595cc7eb6ca8\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":612360,\"participants\":[{\"id\":999552,\"user_id\":null,\"contact_id\":4491,\"lead_id\":null},{\"id\":999553,\"user_id\":206,\"contact_id\":null,\"lead_id\":null},{\"id\":999565,\"user_id\":null,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"b8b554cd-4830-4ac5-be75-595cc7eb6ca8\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"b8b554cd-4830-4ac5-be75-595cc7eb6ca8\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"b8b554cd-4830-4ac5-be75-595cc7eb6ca8\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"b8b554cd-4830-4ac5-be75-595cc7eb6ca8\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"b8b554cd-4830-4ac5-be75-595cc7eb6ca8\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"horencho@gmail.com\"} {\"correlation_id\":\"b8b554cd-4830-4ac5-be75-595cc7eb6ca8\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":612360,\"team_id\":2,\"email\":\"horen.kirazyan@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"b8b554cd-4830-4ac5-be75-595cc7eb6ca8\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [Hubspot] Pagination completed {\"team_id\":2,\"endpoint\":\"https://api.hubapi.com/crm/v3/objects/contact/search\",\"total_requests\":1,\"total_records_fetched\":0,\"total_elapsed_seconds\":0.31,\"average_seconds_per_request\":0.31} {\"correlation_id\":\"b8b554cd-4830-4ac5-be75-595cc7eb6ca8\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":612360,\"participants_processed\":3,\"exact_matches\":1,\"domain_matches\":0,\"best_match_found\":true} {\"correlation_id\":\"b8b554cd-4830-4ac5-be75-595cc7eb6ca8\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612360} {\"correlation_id\":\"b8b554cd-4830-4ac5-be75-595cc7eb6ca8\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612360,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"b8b554cd-4830-4ac5-be75-595cc7eb6ca8\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":612360,\"remote_search\":true,\"lead_id\":null,\"contact_id\":4491,\"account_id\":243,\"opportunity_id\":276,\"stage_id\":36} {\"correlation_id\":\"b8b554cd-4830-4ac5-be75-595cc7eb6ca8\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":612340,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":4491,\"account_id\":243,\"opportunity_id\":276,\"stage_id\":36}} {\"correlation_id\":\"9a47ab79-dbb3-46e2-ad27-3a5120c080ef\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612340} {\"correlation_id\":\"9a47ab79-dbb3-46e2-ad27-3a5120c080ef\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612340,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"9a47ab79-dbb3-46e2-ad27-3a5120c080ef\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":612340,\"participants\":[{\"id\":999516,\"user_id\":null,\"contact_id\":4491,\"lead_id\":null},{\"id\":999517,\"user_id\":206,\"contact_id\":null,\"lead_id\":null},{\"id\":999518,\"user_id\":null,\"contact_id\":null,\"lead_id\":null},{\"id\":999519,\"user_id\":null,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"9a47ab79-dbb3-46e2-ad27-3a5120c080ef\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"9a47ab79-dbb3-46e2-ad27-3a5120c080ef\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"9a47ab79-dbb3-46e2-ad27-3a5120c080ef\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"9a47ab79-dbb3-46e2-ad27-3a5120c080ef\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"9a47ab79-dbb3-46e2-ad27-3a5120c080ef\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"horencho@gmail.com\"} {\"correlation_id\":\"9a47ab79-dbb3-46e2-ad27-3a5120c080ef\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":612340,\"team_id\":2,\"email\":\"horen.kirazyan@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"9a47ab79-dbb3-46e2-ad27-3a5120c080ef\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [Hubspot] Pagination completed {\"team_id\":2,\"endpoint\":\"https://api.hubapi.com/crm/v3/objects/contact/search\",\"total_requests\":1,\"total_records_fetched\":0,\"total_elapsed_seconds\":0.24,\"average_seconds_per_request\":0.24} {\"correlation_id\":\"9a47ab79-dbb3-46e2-ad27-3a5120c080ef\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":612340,\"participants_processed\":4,\"exact_matches\":1,\"domain_matches\":0,\"best_match_found\":true} {\"correlation_id\":\"9a47ab79-dbb3-46e2-ad27-3a5120c080ef\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [ EsUpdateProcessManager ] Finished updating entities in ES {\"worker\":\"\",\"peak_memory\":\"99.73 MB\",\"elapsed_seconds\":0.45,\"update_target\":\"activities\",\"should_iterate_again\":false} {\"correlation_id\":\"38935ae1-fcb3-437e-9c9e-1b7415a0b7e1\",\"trace_id\":\"325a2f9b-9c66-4223-ac02-1a43723f4315\"}\n[2026-05-11 11:06:16] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612340} {\"correlation_id\":\"9a47ab79-dbb3-46e2-ad27-3a5120c080ef\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612340,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"9a47ab79-dbb3-46e2-ad27-3a5120c080ef\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":612340,\"remote_search\":true,\"lead_id\":null,\"contact_id\":4491,\"account_id\":243,\"opportunity_id\":276,\"stage_id\":36} {\"correlation_id\":\"9a47ab79-dbb3-46e2-ad27-3a5120c080ef\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":612339,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":4491,\"account_id\":243,\"opportunity_id\":276,\"stage_id\":36}} {\"correlation_id\":\"eae16400-35f9-4885-b5fc-edb8872350d4\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612339} {\"correlation_id\":\"eae16400-35f9-4885-b5fc-edb8872350d4\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612339,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"eae16400-35f9-4885-b5fc-edb8872350d4\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":612339,\"participants\":[{\"id\":999514,\"user_id\":null,\"contact_id\":4491,\"lead_id\":null},{\"id\":999515,\"user_id\":206,\"contact_id\":null,\"lead_id\":null},{\"id\":999540,\"user_id\":null,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"eae16400-35f9-4885-b5fc-edb8872350d4\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"eae16400-35f9-4885-b5fc-edb8872350d4\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"eae16400-35f9-4885-b5fc-edb8872350d4\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"eae16400-35f9-4885-b5fc-edb8872350d4\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"eae16400-35f9-4885-b5fc-edb8872350d4\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"horencho@gmail.com\"} {\"correlation_id\":\"eae16400-35f9-4885-b5fc-edb8872350d4\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":612339,\"team_id\":2,\"email\":\"horen.kirazyan@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"eae16400-35f9-4885-b5fc-edb8872350d4\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":612339,\"participants_processed\":3,\"exact_matches\":1,\"domain_matches\":0,\"best_match_found\":true} {\"correlation_id\":\"eae16400-35f9-4885-b5fc-edb8872350d4\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612339} {\"correlation_id\":\"eae16400-35f9-4885-b5fc-edb8872350d4\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612339,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"eae16400-35f9-4885-b5fc-edb8872350d4\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":612339,\"remote_search\":true,\"lead_id\":null,\"contact_id\":4491,\"account_id\":243,\"opportunity_id\":276,\"stage_id\":36} {\"correlation_id\":\"eae16400-35f9-4885-b5fc-edb8872350d4\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":612336,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":4491,\"account_id\":243,\"opportunity_id\":276,\"stage_id\":36}} {\"correlation_id\":\"1028998b-3369-4f2f-b7cb-e4ef79db71a5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612336} {\"correlation_id\":\"1028998b-3369-4f2f-b7cb-e4ef79db71a5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612336,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"1028998b-3369-4f2f-b7cb-e4ef79db71a5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":612336,\"participants\":[{\"id\":999508,\"user_id\":null,\"contact_id\":4491,\"lead_id\":null},{\"id\":999509,\"user_id\":206,\"contact_id\":null,\"lead_id\":null},{\"id\":999512,\"user_id\":null,\"contact_id\":null,\"lead_id\":null},{\"id\":999513,\"user_id\":null,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"1028998b-3369-4f2f-b7cb-e4ef79db71a5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"1028998b-3369-4f2f-b7cb-e4ef79db71a5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"1028998b-3369-4f2f-b7cb-e4ef79db71a5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"1028998b-3369-4f2f-b7cb-e4ef79db71a5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"1028998b-3369-4f2f-b7cb-e4ef79db71a5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"horencho@gmail.com\"} {\"correlation_id\":\"1028998b-3369-4f2f-b7cb-e4ef79db71a5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":612336,\"team_id\":2,\"email\":\"horen.kirazyan@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"1028998b-3369-4f2f-b7cb-e4ef79db71a5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":612336,\"participants_processed\":4,\"exact_matches\":1,\"domain_matches\":0,\"best_match_found\":true} {\"correlation_id\":\"1028998b-3369-4f2f-b7cb-e4ef79db71a5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612336} {\"correlation_id\":\"1028998b-3369-4f2f-b7cb-e4ef79db71a5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612336,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"1028998b-3369-4f2f-b7cb-e4ef79db71a5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":612336,\"remote_search\":true,\"lead_id\":null,\"contact_id\":4491,\"account_id\":243,\"opportunity_id\":276,\"stage_id\":36} {\"correlation_id\":\"1028998b-3369-4f2f-b7cb-e4ef79db71a5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":612183,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":4487,\"account_id\":244,\"opportunity_id\":299,\"stage_id\":36}} {\"correlation_id\":\"135ad495-9ac2-4ae2-9428-66f40f3a4731\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612183} {\"correlation_id\":\"135ad495-9ac2-4ae2-9428-66f40f3a4731\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612183,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"135ad495-9ac2-4ae2-9428-66f40f3a4731\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":612183,\"participants\":[{\"id\":999227,\"user_id\":null,\"contact_id\":4487,\"lead_id\":null},{\"id\":999228,\"user_id\":261,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"135ad495-9ac2-4ae2-9428-66f40f3a4731\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"135ad495-9ac2-4ae2-9428-66f40f3a4731\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"135ad495-9ac2-4ae2-9428-66f40f3a4731\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"135ad495-9ac2-4ae2-9428-66f40f3a4731\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"135ad495-9ac2-4ae2-9428-66f40f3a4731\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: ProspectCache - Searching DB for opportunity by owner {\"account_id\":244,\"contact_id\":4487,\"owner_id\":261} {\"correlation_id\":\"135ad495-9ac2-4ae2-9428-66f40f3a4731\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: ProspectCache - Opportunity DB search results {\"account_id\":244,\"contact_id\":4487,\"opportunity_id\":299} {\"correlation_id\":\"135ad495-9ac2-4ae2-9428-66f40f3a4731\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"robinson@crusoe.com\"} {\"correlation_id\":\"135ad495-9ac2-4ae2-9428-66f40f3a4731\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":612183,\"team_id\":2,\"email\":\"adelina.petrova@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"135ad495-9ac2-4ae2-9428-66f40f3a4731\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":612183,\"participants_processed\":2,\"exact_matches\":1,\"domain_matches\":0,\"best_match_found\":true} {\"correlation_id\":\"135ad495-9ac2-4ae2-9428-66f40f3a4731\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612183} {\"correlation_id\":\"135ad495-9ac2-4ae2-9428-66f40f3a4731\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612183,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"135ad495-9ac2-4ae2-9428-66f40f3a4731\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":612183,\"remote_search\":true,\"lead_id\":null,\"contact_id\":4487,\"account_id\":244,\"opportunity_id\":299,\"stage_id\":36} {\"correlation_id\":\"135ad495-9ac2-4ae2-9428-66f40f3a4731\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":612182,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":4487,\"account_id\":244,\"opportunity_id\":299,\"stage_id\":36}} {\"correlation_id\":\"9a2d51b0-64bc-41ea-86b1-8f3bcbb65a89\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612182} {\"correlation_id\":\"9a2d51b0-64bc-41ea-86b1-8f3bcbb65a89\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612182,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"9a2d51b0-64bc-41ea-86b1-8f3bcbb65a89\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":612182,\"participants\":[{\"id\":999225,\"user_id\":null,\"contact_id\":4487,\"lead_id\":null},{\"id\":999226,\"user_id\":261,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"9a2d51b0-64bc-41ea-86b1-8f3bcbb65a89\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"mailbox:batch:process\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.727} {\"correlation_id\":\"dceccba8-9e9c-476e-993d-809067884ebe\",\"trace_id\":\"66f8bba8-1712-464b-b25a-418f16736be2\"}\n[2026-05-11 11:06:17] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"9a2d51b0-64bc-41ea-86b1-8f3bcbb65a89\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"9a2d51b0-64bc-41ea-86b1-8f3bcbb65a89\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"9a2d51b0-64bc-41ea-86b1-8f3bcbb65a89\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"9a2d51b0-64bc-41ea-86b1-8f3bcbb65a89\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"robinson@crusoe.com\"} {\"correlation_id\":\"9a2d51b0-64bc-41ea-86b1-8f3bcbb65a89\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":612182,\"team_id\":2,\"email\":\"adelina.petrova@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"9a2d51b0-64bc-41ea-86b1-8f3bcbb65a89\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":612182,\"participants_processed\":2,\"exact_matches\":1,\"domain_matches\":0,\"best_match_found\":true} {\"correlation_id\":\"9a2d51b0-64bc-41ea-86b1-8f3bcbb65a89\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612182} {\"correlation_id\":\"9a2d51b0-64bc-41ea-86b1-8f3bcbb65a89\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612182,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"9a2d51b0-64bc-41ea-86b1-8f3bcbb65a89\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [EmailSchedule] STARTING batch process {\"host\":\"docker_lamp_1\"} {\"correlation_id\":\"dceccba8-9e9c-476e-993d-809067884ebe\",\"trace_id\":\"66f8bba8-1712-464b-b25a-418f16736be2\"}\n[2026-05-11 11:06:17] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":612182,\"remote_search\":true,\"lead_id\":null,\"contact_id\":4487,\"account_id\":244,\"opportunity_id\":299,\"stage_id\":36} {\"correlation_id\":\"9a2d51b0-64bc-41ea-86b1-8f3bcbb65a89\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [EmailSchedule] FINISHED batch process {\"host\":\"docker_lamp_1\",\"processed\":0} {\"correlation_id\":\"dceccba8-9e9c-476e-993d-809067884ebe\",\"trace_id\":\"66f8bba8-1712-464b-b25a-418f16736be2\"}\n[2026-05-11 11:06:17] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage for command {\"command\":\"mailbox:batch:process\",\"memoryBeforeCommandInMb\":60.0,\"memoryAfterCommandInMB\":60.0,\"memoryPeakBeforeCommandInMb\":99.727,\"memoryPeakAfterCommandInMB\":99.727} {\"correlation_id\":\"dceccba8-9e9c-476e-993d-809067884ebe\",\"trace_id\":\"66f8bba8-1712-464b-b25a-418f16736be2\"}\n[2026-05-11 11:06:17] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":612181,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":4487,\"account_id\":244,\"opportunity_id\":299,\"stage_id\":36}} {\"correlation_id\":\"8ff40f83-337f-432a-b950-e35779bb59bf\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612181} {\"correlation_id\":\"8ff40f83-337f-432a-b950-e35779bb59bf\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612181,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"8ff40f83-337f-432a-b950-e35779bb59bf\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":612181,\"participants\":[{\"id\":999223,\"user_id\":null,\"contact_id\":4487,\"lead_id\":null},{\"id\":999224,\"user_id\":261,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"8ff40f83-337f-432a-b950-e35779bb59bf\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"8ff40f83-337f-432a-b950-e35779bb59bf\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"8ff40f83-337f-432a-b950-e35779bb59bf\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"8ff40f83-337f-432a-b950-e35779bb59bf\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"8ff40f83-337f-432a-b950-e35779bb59bf\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"robinson@crusoe.com\"} {\"correlation_id\":\"8ff40f83-337f-432a-b950-e35779bb59bf\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":612181,\"team_id\":2,\"email\":\"adelina.petrova@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"8ff40f83-337f-432a-b950-e35779bb59bf\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":612181,\"participants_processed\":2,\"exact_matches\":1,\"domain_matches\":0,\"best_match_found\":true} {\"correlation_id\":\"8ff40f83-337f-432a-b950-e35779bb59bf\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612181} {\"correlation_id\":\"8ff40f83-337f-432a-b950-e35779bb59bf\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612181,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"8ff40f83-337f-432a-b950-e35779bb59bf\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":612181,\"remote_search\":true,\"lead_id\":null,\"contact_id\":4487,\"account_id\":244,\"opportunity_id\":299,\"stage_id\":36} {\"correlation_id\":\"8ff40f83-337f-432a-b950-e35779bb59bf\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":612180,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":4487,\"account_id\":244,\"opportunity_id\":299,\"stage_id\":36}} {\"correlation_id\":\"03f05b91-f803-45c9-b4bf-6a5f8ddf4a2c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612180} {\"correlation_id\":\"03f05b91-f803-45c9-b4bf-6a5f8ddf4a2c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612180,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"03f05b91-f803-45c9-b4bf-6a5f8ddf4a2c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":612180,\"participants\":[{\"id\":999221,\"user_id\":null,\"contact_id\":4487,\"lead_id\":null},{\"id\":999222,\"user_id\":261,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"03f05b91-f803-45c9-b4bf-6a5f8ddf4a2c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"03f05b91-f803-45c9-b4bf-6a5f8ddf4a2c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"03f05b91-f803-45c9-b4bf-6a5f8ddf4a2c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"03f05b91-f803-45c9-b4bf-6a5f8ddf4a2c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"03f05b91-f803-45c9-b4bf-6a5f8ddf4a2c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"robinson@crusoe.com\"} {\"correlation_id\":\"03f05b91-f803-45c9-b4bf-6a5f8ddf4a2c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":612180,\"team_id\":2,\"email\":\"adelina.petrova@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"03f05b91-f803-45c9-b4bf-6a5f8ddf4a2c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":612180,\"participants_processed\":2,\"exact_matches\":1,\"domain_matches\":0,\"best_match_found\":true} {\"correlation_id\":\"03f05b91-f803-45c9-b4bf-6a5f8ddf4a2c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612180} {\"correlation_id\":\"03f05b91-f803-45c9-b4bf-6a5f8ddf4a2c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612180,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"03f05b91-f803-45c9-b4bf-6a5f8ddf4a2c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":612180,\"remote_search\":true,\"lead_id\":null,\"contact_id\":4487,\"account_id\":244,\"opportunity_id\":299,\"stage_id\":36} {\"correlation_id\":\"03f05b91-f803-45c9-b4bf-6a5f8ddf4a2c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":611455,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89}} {\"correlation_id\":\"299c67e4-7026-46ee-b10d-d4e48fb55a7c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":611455} {\"correlation_id\":\"299c67e4-7026-46ee-b10d-d4e48fb55a7c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":611455,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"299c67e4-7026-46ee-b10d-d4e48fb55a7c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":611455,\"participants\":[{\"id\":997961,\"user_id\":null,\"contact_id\":null,\"lead_id\":null},{\"id\":997962,\"user_id\":1460,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"299c67e4-7026-46ee-b10d-d4e48fb55a7c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"299c67e4-7026-46ee-b10d-d4e48fb55a7c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"299c67e4-7026-46ee-b10d-d4e48fb55a7c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"299c67e4-7026-46ee-b10d-d4e48fb55a7c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"299c67e4-7026-46ee-b10d-d4e48fb55a7c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [Prospect match] Cache miss, calling the API {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"299c67e4-7026-46ee-b10d-d4e48fb55a7c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [Hubspot] Failed to fetch contact {\"email\":\"support@staging.jiminny.com\",\"reason\":\"[404] Client error: `GET https://api.hubapi.com/crm/v3/objects/contacts/support%40staging.jiminny.com?properties=email%2Cfirstname%2Clastname%2Ccountry%2Cphone%2Cmobilephone%2Cjobtitle%2Chubspot_owner_id%2Cassociatedcompanyid%2Cphoto&archived=0&idProperty=email` resulted in a `404 Not Found` response\"} {\"correlation_id\":\"299c67e4-7026-46ee-b10d-d4e48fb55a7c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [Prospect match] API returned empty result, caching the miss with empty prospect data {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"299c67e4-7026-46ee-b10d-d4e48fb55a7c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [Hubspot] Pagination completed {\"team_id\":2,\"endpoint\":\"https://api.hubapi.com/crm/v3/objects/contact/search\",\"total_requests\":1,\"total_records_fetched\":0,\"total_elapsed_seconds\":0.24,\"average_seconds_per_request\":0.24} {\"correlation_id\":\"299c67e4-7026-46ee-b10d-d4e48fb55a7c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [Prospect match] Resolved company domain from email {\"email\":\"support@staging.jiminny.com\",\"domain\":\"jiminny.com\"} {\"correlation_id\":\"299c67e4-7026-46ee-b10d-d4e48fb55a7c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [Prospect match] Cache miss {\"identifier_type\":\"domain\",\"identifier\":\"jiminny.com\",\"crm\":\"hubspot\"} {\"correlation_id\":\"299c67e4-7026-46ee-b10d-d4e48fb55a7c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [Prospect match] Cache miss, calling the API {\"identifier_type\":\"domain\",\"identifier\":\"jiminny.com\"} {\"correlation_id\":\"299c67e4-7026-46ee-b10d-d4e48fb55a7c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:19] local.INFO: [HubSpot] importAccount {\"crm_provider_id\":\"749766179\",\"config_id\":2} {\"correlation_id\":\"299c67e4-7026-46ee-b10d-d4e48fb55a7c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:19] local.INFO: [HubSpot] CRM Search requested {\"request\":{\"filterGroups\":[{\"filters\":[{\"propertyName\":\"associations.company\",\"operator\":\"EQ\",\"value\":\"749766179\"},{\"propertyName\":\"dealstage\",\"operator\":\"NOT_IN\",\"values\":[\"closedwon\",\"4040964\",\"59247967\"]},{\"propertyName\":\"dealstage\",\"operator\":\"NOT_IN\",\"values\":[\"closedlost\",\"4040965\",\"59247968\"]}]}],\"sorts\":[{\"propertyName\":\"modifieddate\",\"direction\":\"DESCENDING\"}],\"properties\":[\"dealname\",\"amount\",\"hubspot_owner_id\",\"pipeline\",\"dealstage\",\"closedate\",\"deal_currency_code\"],\"limit\":200}} {\"correlation_id\":\"299c67e4-7026-46ee-b10d-d4e48fb55a7c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:19] local.INFO: [Hubspot] Pagination completed {\"team_id\":2,\"endpoint\":\"https://api.hubapi.com/crm/v3/objects/deals/search\",\"total_requests\":1,\"total_records_fetched\":10,\"total_elapsed_seconds\":0.34,\"average_seconds_per_request\":0.34} {\"correlation_id\":\"299c67e4-7026-46ee-b10d-d4e48fb55a7c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}","role_description":"text entry area","is_enabled":true,"is_focused":true,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"bounds":{"left":0.011968086,"top":0.047885075,"width":0.024268618,"height":0.024740623},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
7868505355964241909
|
4180792001263151229
|
visual_change
|
accessibility
|
NULL
|
Project: faVsco.js, menu
JY-20725-handle-HS-search Project: faVsco.js, menu
JY-20725-handle-HS-search-rate-limit, menu
Start Listening for PHP Debug Connections
HandleHubspotRateLimitTest
Run 'HandleHubspotRateLimitTest'
Debug 'HandleHubspotRateLimitTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
5
133
11
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Console\Commands;
use Carbon\Carbon;
use Carbon\CarbonImmutable;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Redis;
use InvalidArgumentException;
use Jiminny\Jobs\AutomatedReports\RequestGenerateAskJiminnyReportJob;
use Jiminny\Jobs\AutomatedReports\SendReportMailJob;
use Jiminny\Jobs\Crm\Delete\VerifyActivityCrmTaskJob;
use Jiminny\Jobs\Crm\MatchActivityCrmData;
use Jiminny\Jobs\JobDispatcherInterface;
use Jiminny\Models\Activity;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\AutomatedReportResult;
use Jiminny\Models\Team;
use Jiminny\Models\User;
use Jiminny\Repositories\AutomatedReportsRepository;
use Jiminny\Services\Activity\CrmOwnerResolver;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
/**
* Class JiminnyDebugCommand
*
* @package Jiminny\Console\Commands
*/
class JiminnyDebugCommand extends Command
{
public const string FREQUENCY_DAILY = 'daily';
public const string FREQUENCY_WEEKLY = 'weekly';
public const string FREQUENCY_MONTHLY = 'monthly';
public const string FREQUENCY_QUARTERLY = 'quarterly';
public const string FREQUENCY_ONE_OFF = 'one_off';
protected $signature = 'jiminny:debug';
public function handle(
JobDispatcherInterface $jobDispatcher,
AutomatedReportsService $automatedReportsService,
AutomatedReportsRepository $automatedReportsRepository,
UserPilotClient $userPilotClient
): void {
// Choose ONE of the following to run, then comment out the others.
// 1) Dispatch a storm of MatchActivityCrmData jobs against team 2
$this->simulateMatchActivityStorm(teamId: 2, count: 100);
// 2) Dispatch a storm of VerifyActivityCrmTaskJob jobs (simulates DeleteCrmEntityTrait fan-out)
// $this->simulateVerifyTaskStorm(teamId: 2, count: 100);
// 3) Inspect Redis circuit-breaker state for the team's HubSpot portal
// $this->observeRateLimitCache(teamId: 2);
// 4) Make 3 synchronous matchByName calls (foreground, hits API directly)
// $this->rateLimit();
exit(1);
$report = AutomatedReport::find(71);
$last = AutomatedReportResult::query()
->where('report_id', $report->getId())
->whereIn('status', [AutomatedReportResult::STATUS_DEFAULT, AutomatedReportResult::STATUS_FAILED])
// ->where('reason', '!=', AutomatedReportResult::REASON_NOT_ENOUGH_ACTIVITIES)
->whereDate('created_at', CarbonImmutable::now()->toDateString())
->latest()
->first();
$this->info("Last: {$last->getId()}");
exit(1);
$user = User::find(143);
// $count = $automatedReportsRepository->countUserReports($user);
// $this->info("Count: {$count}");
// $count = $automatedReportsRepository->countAllUserReports($user);
// $this->info("All count: {$count}");
$payload = [
'report_type' => 'ask_jiminny',
'frequency' => 'weekly',
];
$userPilotClient->track($user, 'ask-jiminny-report-generated', $payload);
exit(1);
$now = Carbon::now()->subDay(1);
$this->info("Now: {$now->toDateTimeString()}");
$weekStart = Carbon::getWeekStartsAt();
$this->info("Now: {$weekStart}");
// $from = $now->copy()->previousWeekday()->startOfDay();
// $to = $now->copy()->previousWeekday()->endOfDay();
// $fromOld = $now->copy()->subWeeks(1)->startOfDay();
// $toOld = $now->copy()->subDay()->endOfDay();
// $fromNew = $now->copy()->subWeek()->startOfWeek();
// $toNew = $now->copy()->subWeek()->endOfWeek();
// $fromOld = $now->copy()->subMonths(1)->startOfDay();
// $toOld = $now->copy()->subDay()->endOfDay();
// $fromNew = $now->copy()->subMonthNoOverflow()->startOfMonth();
// $toNew = $now->copy()->subMonthNoOverflow()->endOfMonth();
$fromOld = $now->copy()->subMonths(3)->startOfDay();
$toOld = $now->copy()->subDay()->endOfDay();
$fromNew = $now->copy()->subQuarterNoOverflow()->startOfQuarter();
$toNew = $now->copy()->subQuarterNoOverflow()->endOfQuarter();
$this->info("From old: {$fromOld->toDateTimeString()}");
$this->info("To old: {$toOld->toDateTimeString()}");
$this->info("From new: {$fromNew->toDateTimeString()}");
$this->info("To new: {$toNew->toDateTimeString()}");
exit(1);
$report = AutomatedReport::find(71);
$job = new RequestGenerateAskJiminnyReportJob($report->getUuid());
$jobDispatcher->dispatch($job);
exit(1);
// $this->formatDate($jobDispatcher);
// $this->sendMail($jobDispatcher, $automatedReportsService);
// $this->crmService();
$this->getPayload($automatedReportsService);
exit(1);
}
private function crmService()
{
$activity = Activity::find(418141);
$team = Team::find(19);
$config = $team->getCrmConfiguration();
$crmResolver = app(CrmOwnerResolver::class, [
'team' => $team,
'integrationAdmin' => $team->getOwner(),
'providerSlug' => $config->getProviderName(),
]);
$crmService = $crmResolver->prepareCrmService();
$crmService->createTranscriptNotes($activity);
}
private function sendMail(JobDispatcherInterface $jobDispatcher, AutomatedReportsService $automatedReportsService)
{
$reportUuid = '';
// $report = $automatedReportsService->getReportResult($reportUuid);
$report = AutomatedReportResult::find(275);
$validRecipients = $automatedReportsService->getValidRecipientUsers(
$report->getReport(),
includeJiminny: true,
);
$recipient = $validRecipients[0];
$fileName = $automatedReportsService->getReportFileName($report);
$typeName = $report->getReport()->getCustomName()
?? $automatedReportsService->getReportTypeName($report);
$teamsName = $automatedReportsService->getReportTeamsName($report);
$periodName = $automatedReportsService->getReportPeriodName($report);
$s3Path = $automatedReportsService->getMediaPath($report);
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$fileName ' . PHP_EOL . print_r($fileName, true));
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$typeName ' . PHP_EOL . print_r($typeName, true));
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$teamsName ' . PHP_EOL . print_r($teamsName, true));
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$periodName ' . PHP_EOL . print_r($periodName, true));
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$s3Path ' . PHP_EOL . print_r($s3Path, true));
$jobDispatcher->dispatch(
new SendReportMailJob(
reportUuid: $report->getUuid(),
s3Path: $s3Path,
recipientEmail: $recipient['email'],
recipientName: $recipient['name'] ?? null,
fileName: $fileName,
typeName: $typeName,
teamsName: $teamsName,
periodName: $periodName,
isAskJiminny: true,
)
);
exit(1);
}
private function formatDate(JobDispatcherInterface $jobDispatcher): void
{
$customName = 'Custom report name';
// $frequency = self::FREQUENCY_DAILY;
// $frequency = self::FREQUENCY_WEEKLY;
$frequency = self::FREQUENCY_MONTHLY;
// $frequency = self::FREQUENCY_QUARTERLY;
// $frequency = self::FREQUENCY_ONE_OFF;
$period = $this->calculateFromAndToDatePeriod($frequency);
$from = $period['fromDate'];
$to = $period['toDate'];
$periodName = $this->formatReportPeriodName($frequency, $from, $to);
$filenameSuffix = null;
if ($customName) {
if ($filenameSuffix) {
$customName .= " {$filenameSuffix}";
}
$result = $this->sanitizeFileName("{$customName} - {$periodName}");
}
$this->info($result);
}
public function calculateFromAndToDatePeriod(
string $frequency,
?Carbon $fromDate = null,
?Carbon $toDate = null
): array {
if ($frequency === self::FREQUENCY_ONE_OFF) {
return [
'fromDate' => $fromDate,
'toDate' => $toDate,
];
}
$now = Carbon::now();
return match ($frequency) {
self::FREQUENCY_DAILY => [
'fromDate' => $now->copy()->subDay()->startOfDay(),
'toDate' => $now->copy()->subDay()->endOfDay(),
],
self::FREQUENCY_WEEKLY => [
'fromDate' => $now->copy()->subWeeks(1)->startOfDay(),
'toDate' => $now->copy()->subDay()->endOfDay(),
],
self::FREQUENCY_MONTHLY => [
'fromDate' => $now->copy()->subMonths(1)->startOfDay(),
'toDate' => $now->copy()->subDay()->endOfDay(),
],
self::FREQUENCY_QUARTERLY => [
'fromDate' => $now->copy()->subMonths(3)->startOfDay(),
'toDate' => $now->copy()->subDay()->endOfDay(),
],
default => throw new InvalidArgumentException("Unsupported frequency: {$frequency}"),
};
}
private function formatReportPeriodName(string $frequency, Carbon $from, Carbon $to): string
{
$fromYear = $from->format('Y');
$toYear = $to->format('Y');
$differentYears = $fromYear !== $toYear;
switch ($frequency) {
case self::FREQUENCY_DAILY:
return $from->format('j M Y');
case self::FREQUENCY_QUARTERLY:
// 'Jan-Mar 2025' or 'Nov 2024-Jan 2025' if years differ
$startMonth = $from->format('M');
$endMonth = $to->copy()->subMonth();
$endMonthName = $endMonth->format('M');
$endMonthYear = $endMonth->format('Y');
if ($differentYears) {
return "{$startMonth} {$fromYear} - {$endMonthName} {$endMonthYear}";
}
return "{$startMonth} - {$endMonthName} {$toYear}";
case self::FREQUENCY_MONTHLY:
// 'May 2025' - monthly reports are always within the same year
return $from->format('M Y');
case self::FREQUENCY_WEEKLY:
// '4 - 8 Aug 2025', '27 Oct - 3 Nov 2025', or '28 Dec 2024 - 3 Jan 2025' if years differ
$startDay = $from->format('j');
$endDay = $to->format('j');
$startMonth = $from->format('M');
$endMonth = $to->format('M');
if ($differentYears) {
return "{$startDay} {$startMonth} {$fromYear} - {$endDay} {$endMonth} {$toYear}";
}
if ($startMonth !== $endMonth) {
return "{$startDay} {$startMonth} - {$endDay} {$endMonth} {$toYear}";
}
return "{$startDay} - {$endDay} {$endMonth} {$toYear}";
case self::FREQUENCY_ONE_OFF:
// '2 May-31 May 2025' or '15 Dec 2024-15 Jan 2025' if years differ
$startDay = $from->format('j');
$startMonth = $from->format('M');
$endDay = $to->format('j');
$endMonth = $to->format('M');
// If same month and year, use a format like '2-31 May 2025'
if ($startMonth === $endMonth && ! $differentYears) {
return "{$startDay} - {$endDay} {$startMonth} {$toYear}";
}
// If different years, include both years
if ($differentYears) {
return "{$startDay} {$startMonth} {$fromYear} - {$endDay} {$endMonth} {$toYear}";
}
// Same year but different months
return "{$startDay} {$startMonth} - {$endDay} {$endMonth} {$toYear}";
default:
// Default format for unknown frequencies
return $from->format('j M Y') . ' - ' . $to->format('j M Y');
}
}
public function sanitizeFileName(string $fileName): string
{
return str_replace(['/', '\\'], '-', $fileName);
}
private function getPayload(AutomatedReportsService $automatedReportsService)
{
$reportResult = AutomatedReportResult::find(269);
$automatedReport = $reportResult->getReport();
$activityIds = [1,2,3];
$payload = $automatedReportsService->getAskJiminnyGenerateReportPayload(
automatedReport: $automatedReport,
reportResult: $reportResult,
activityIds: $activityIds,
);
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$payload ' . PHP_EOL . print_r($payload, true));
}
private function rateLimit()
{
$team = Team::find(2);
$config = $team->getCrmConfiguration();
$crmResolver = app(CrmOwnerResolver::class, [
'team' => $team,
'integrationAdmin' => $team->getOwner(),
'providerSlug' => $config->getProviderName(),
]);
$crmService = $crmResolver->prepareCrmService();
for ($i = 0 ; $i < 3; $i++) {
// if ($i % 25 === 0) {
// $this->info("Syncing opportunity {$i}");
$this->info("Matching contact {$i}");
// }
// $crmService->syncOpportunity('374720564');
$crmService->matchByName('Robot');
}
}
private function simulateMatchActivityStorm(int $teamId = 2, int $count = 100): void
{
$team = Team::find($teamId);
$config = $team->getCrmConfiguration();
$activities = Activity::query()
->where('crm_configuration_id', $config->getId())
->orderByDesc('id')
->limit($count)
->get();
$this->info("Dispatching {$activities->count()} MatchActivityCrmData jobs (portal={$config->getId()})");
foreach ($activities as $activity) {
MatchActivityCrmData::dispatch($activity->getId(), $config, true);
}
$this->info('Done. Watch logs and run jiminny:debug observeRateLimit to inspect cache state.');
}
private function simulateVerifyTaskStorm(int $teamId = 2, int $count = 100): void
{
$activities = Activity::query()
->where('team_id', $teamId)
->whereNotNull('crm_provider_id')
->orderByDesc('id')
->limit($count)
->get();
$this->info("Dispatching {$activities->count()} VerifyActivityCrmTaskJob jobs");
foreach ($activities as $activity) {
VerifyActivityCrmTaskJob::dispatch($activity->getId());
}
$this->info('Done.');
}
private function observeRateLimitCache(int $teamId = 2): void
{
$team = Team::find($teamId);
$config = $team->getCrmConfiguration();
$key = sprintf('hubspot:ratelimit:portal:%d', $config->getId());
$value = Redis::get($key);
$ttl = Redis::ttl($key);
$this->info("Redis key: {$key}");
$this->info('Value: ' . ($value ?? '(empty)'));
$this->info("TTL: {$ttl}s");
}
}
Show Replace Field
Search History
429
New Line
Match Case
Words
Regex
Replace History
Replace
New Line
Preserve case
0 results
Previous Occurrence
Next Occurrence
Filter Search Results
Open in Window, Multiple Cursors
Click to highlight
Close
Sync Changes
Hide This Notification
Code changed:
Hide
124
Previous Highlighted Error
Next Highlighted Error
[2026-05-11 11:06:08] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {"activity_id":612562,"team_id":2,"email":"[EMAIL]"} {"correlation_id":"c1949c5e-c0f4-431e-ba26-59d49408d871","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:08] local.INFO: [CrmActivityService] CRM matching completed {"activity_id":612562,"participants_processed":2,"exact_matches":0,"domain_matches":1,"best_match_found":true} {"correlation_id":"c1949c5e-c0f4-431e-ba26-59d49408d871","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:08] local.INFO: [ EsUpdateTarget ] Update single target {"target":"activities","purpose":"searchable-observer-update","entityId":612562} {"correlation_id":"c1949c5e-c0f4-431e-ba26-59d49408d871","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:08] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {"entityType":"activities","entityId":612562,"collectionKey":"activities-for-update-priority","withPriority":true} {"correlation_id":"c1949c5e-c0f4-431e-ba26-59d49408d871","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:08] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {"activity":612562,"remote_search":true,"lead_id":null,"contact_id":null,"account_id":26,"opportunity_id":22,"stage_id":89} {"correlation_id":"c1949c5e-c0f4-431e-ba26-59d49408d871","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:09] local.INFO: [MatchActivityCrmData] Starting CRM data matching {"activity":612561,"remote_search":true,"set_configuration":2,"old_state":{"lead_id":null,"contact_id":null,"account_id":26,"opportunity_id":22,"stage_id":89}} {"correlation_id":"b715f531-04da-410f-be31-055aa1c13754","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:09] local.INFO: [ EsUpdateTarget ] Update single target {"target":"activities","purpose":"searchable-observer-update","entityId":612561} {"correlation_id":"b715f531-04da-410f-be31-055aa1c13754","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:09] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {"entityType":"activities","entityId":612561,"collectionKey":"activities-for-update-priority","withPriority":true} {"correlation_id":"b715f531-04da-410f-be31-055aa1c13754","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:09] local.INFO: [MatchActivityCrmData] Participants old state {"activity":612561,"participants":[{"id":999780,"user_id":206,"contact_id":null,"lead_id":null},{"id":999781,"user_id":null,"contact_id":null,"lead_id":null}]} {"correlation_id":"b715f531-04da-410f-be31-055aa1c13754","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:09] local.INFO: [SocialAccountService] Fetching token {"socialAccountId":1499,"provider":"hubspot"} {"correlation_id":"b715f531-04da-410f-be31-055aa1c13754","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:09] local.INFO: [SocialAccountService] Token retrieved {"socialAccountId":1499,"provider":"hubspot"} {"correlation_id":"b715f531-04da-410f-be31-055aa1c13754","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:09] local.INFO: [EncryptedTokenManager] Generating access token. {"mode":"legacy"} {"correlation_id":"b715f531-04da-410f-be31-055aa1c13754","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:09] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {"crm_provider":"hubspot","crm_owner":148,"team_id":2} {"correlation_id":"b715f531-04da-410f-be31-055aa1c13754","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:09] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {"activity_id":612561,"team_id":2,"email":"[EMAIL]"} {"correlation_id":"b715f531-04da-410f-be31-055aa1c13754","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:09] local.INFO: [Prospect match] Cache miss, calling the API {"identifier_type":"email","identifier":"[EMAIL]"} {"correlation_id":"b715f531-04da-410f-be31-055aa1c13754","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:10] local.INFO: [Hubspot] Failed to fetch contact {"email":"[EMAIL]","reason":"[404] Client error: `GET https://api.hubapi.com/crm/v3/objects/contacts/447700174614.447782589921.OeREojLVnk%40txt.staging.jiminny.com?properties=email%2Cfirstname%2Clastname%2Ccountry%2Cphone%2Cmobilephone%2Cjobtitle%2Chubspot_owner_id%2Cassociatedcompanyid%2Cphoto&archived=0&idProperty=email` resulted in a `404 Not Found` response"} {"correlation_id":"b715f531-04da-410f-be31-055aa1c13754","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:10] local.INFO: [Prospect match] API returned empty result, caching the miss with empty prospect data {"identifier_type":"email","identifier":"[EMAIL]"} {"correlation_id":"b715f531-04da-410f-be31-055aa1c13754","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:10] local.INFO: [Prospect match] Resolved company domain from email {"email":"[EMAIL]","domain":"jiminny.com"} {"correlation_id":"b715f531-04da-410f-be31-055aa1c13754","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:10] local.INFO: [CrmActivityService] CRM matching completed {"activity_id":612561,"participants_processed":2,"exact_matches":0,"domain_matches":1,"best_match_found":true} {"correlation_id":"b715f531-04da-410f-be31-055aa1c13754","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:10] local.INFO: [ EsUpdateTarget ] Update single target {"target":"activities","purpose":"searchable-observer-update","entityId":612561} {"correlation_id":"b715f531-04da-410f-be31-055aa1c13754","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:10] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {"entityType":"activities","entityId":612561,"collectionKey":"activities-for-update-priority","withPriority":true} {"correlation_id":"b715f531-04da-410f-be31-055aa1c13754","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:10] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {"activity":612561,"remote_search":true,"lead_id":null,"contact_id":null,"account_id":26,"opportunity_id":22,"stage_id":89} {"correlation_id":"b715f531-04da-410f-be31-055aa1c13754","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:10] local.INFO: [MatchActivityCrmData] Starting CRM data matching {"activity":612560,"remote_search":true,"set_configuration":2,"old_state":{"lead_id":null,"contact_id":null,"account_id":26,"opportunity_id":22,"stage_id":89}} {"correlation_id":"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:10] local.INFO: [ EsUpdateTarget ] Update single target {"target":"activities","purpose":"searchable-observer-update","entityId":612560} {"correlation_id":"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:10] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {"entityType":"activities","entityId":612560,"collectionKey":"activities-for-update-priority","withPriority":true} {"correlation_id":"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:10] local.INFO: [MatchActivityCrmData] Participants old state {"activity":612560,"participants":[{"id":999778,"user_id":null,"contact_id":null,"lead_id":null},{"id":999779,"user_id":206,"contact_id":null,"lead_id":null}]} {"correlation_id":"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [SocialAccountService] Fetching token {"socialAccountId":1499,"provider":"hubspot"} {"correlation_id":"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [SocialAccountService] Token retrieved {"socialAccountId":1499,"provider":"hubspot"} {"correlation_id":"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [EncryptedTokenManager] Generating access token. {"mode":"legacy"} {"correlation_id":"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {"crm_provider":"hubspot","crm_owner":148,"team_id":2} {"correlation_id":"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [Prospect match] Cache / local search hit {"identifier_type":"email","identifier":"[EMAIL]"} {"correlation_id":"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [Prospect match] cached empty result - no API calls, try next matching method {"identifier_type":"email","identifier":"[EMAIL]"} {"correlation_id":"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [Prospect match] Resolved company domain from email {"email":"[EMAIL]","domain":"jiminny.com"} {"correlation_id":"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {"activity_id":612560,"team_id":2,"email":"[EMAIL]"} {"correlation_id":"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [CrmActivityService] CRM matching completed {"activity_id":612560,"participants_processed":2,"exact_matches":0,"domain_matches":1,"best_match_found":true} {"correlation_id":"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [ EsUpdateTarget ] Update single target {"target":"activities","purpose":"searchable-observer-update","entityId":612560} {"correlation_id":"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [ EsUpdateProcessManager ] Finished updating entities in ES {"worker":"","peak_memory":"99.73 MB","elapsed_seconds":0.74,"update_target":"activities","should_iterate_again":false} {"correlation_id":"38935ae1-fcb3-437e-9c9e-1b7415a0b7e1","trace_id":"325a2f9b-9c66-4223-ac02-1a43723f4315"}
[2026-05-11 11:06:11] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {"entityType":"activities","entityId":612560,"collectionKey":"activities-for-update-priority","withPriority":true} {"correlation_id":"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {"activity":612560,"remote_search":true,"lead_id":null,"contact_id":null,"account_id":26,"opportunity_id":22,"stage_id":89} {"correlation_id":"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [MatchActivityCrmData] Starting CRM data matching {"activity":612559,"remote_search":true,"set_configuration":2,"old_state":{"lead_id":null,"contact_id":97,"account_id":69,"opportunity_id":5011,"stage_id":34}} {"correlation_id":"4d59a59c-38a5-4a1d-9379-6ceaf67b747e","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [ EsUpdateTarget ] Update single target {"target":"activities","purpose":"searchable-observer-update","entityId":612559} {"correlation_id":"4d59a59c-38a5-4a1d-9379-6ceaf67b747e","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {"entityType":"activities","entityId":612559,"collectionKey":"activities-for-update-priority","withPriority":true} {"correlation_id":"4d59a59c-38a5-4a1d-9379-6ceaf67b747e","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [MatchActivityCrmData] Participants old state {"activity":612559,"participants":[{"id":999776,"user_id":206,"contact_id":null,"lead_id":null},{"id":999777,"user_id":null,"contact_id":97,"lead_id":null}]} {"correlation_id":"4d59a59c-38a5-4a1d-9379-6ceaf67b747e","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [SocialAccountService] Fetching token {"socialAccountId":1499,"provider":"hubspot"} {"correlation_id":"4d59a59c-38a5-4a1d-9379-6ceaf67b747e","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [SocialAccountService] Token retrieved {"socialAccountId":1499,"provider":"hubspot"} {"correlation_id":"4d59a59c-38a5-4a1d-9379-6ceaf67b747e","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [EncryptedTokenManager] Generating access token. {"mode":"legacy"} {"correlation_id":"4d59a59c-38a5-4a1d-9379-6ceaf67b747e","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {"crm_provider":"hubspot","crm_owner":148,"team_id":2} {"correlation_id":"4d59a59c-38a5-4a1d-9379-6ceaf67b747e","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {"activity_id":612559,"team_id":2,"email":"[EMAIL]"} {"correlation_id":"4d59a59c-38a5-4a1d-9379-6ceaf67b747e","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: ProspectCache - Searching DB for opportunity by owner {"account_id":69,"contact_id":97,"owner_id":206} {"correlation_id":"4d59a59c-38a5-4a1d-9379-6ceaf67b747e","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: ProspectCache - Fallback DB opportunity search {"account_id":69,"contact_id":97} {"correlation_id":"4d59a59c-38a5-4a1d-9379-6ceaf67b747e","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: ProspectCache - Opportunity DB search results {"account_id":69,"contact_id":97,"opportunity_id":5011} {"correlation_id":"4d59a59c-38a5-4a1d-9379-6ceaf67b747e","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [Prospect match] Cache / local search hit {"identifier_type":"email","identifier":"[EMAIL]"} {"correlation_id":"4d59a59c-38a5-4a1d-9379-6ceaf67b747e","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [CrmActivityService] CRM matching completed {"activity_id":612559,"participants_processed":2,"exact_matches":1,"domain_matches":0,"best_match_found":true} {"correlation_id":"4d59a59c-38a5-4a1d-9379-6ceaf67b747e","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [ EsUpdateTarget ] Update single target {"target":"activities","purpose":"searchable-observer-update","entityId":612559} {"correlation_id":"4d59a59c-38a5-4a1d-9379-6ceaf67b747e","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {"entityType":"activities","entityId":612559,"collectionKey":"activities-for-update-priority","withPriority":true} {"correlation_id":"4d59a59c-38a5-4a1d-9379-6ceaf67b747e","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {"activity":612559,"remote_search":true,"lead_id":null,"contact_id":97,"account_id":69,"opportunity_id":5011,"stage_id":34} {"correlation_id":"4d59a59c-38a5-4a1d-9379-6ceaf67b747e","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:12] local.INFO: [MatchActivityCrmData] Starting CRM data matching {"activity":612558,"remote_search":true,"set_configuration":2,"old_state":{"lead_id":null,"contact_id":97,"account_id":69,"opportunity_id":5011,"stage_id":34}} {"correlation_id":"a105800d-d2f7-4772-a534-7791e262461c","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:12] local.INFO: [ EsUpdateTarget ] Update single target {"target":"activities","purpose":"searchable-observer-update","entityId":612558} {"correlation_id":"a105800d-d2f7-4772-a534-7791e262461c","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:12] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {"entityType":"activities","entityId":612558,"collectionKey":"activities-for-update-priority","withPriority":true} {"correlation_id":"a105800d-d2f7-4772-a534-7791e262461c","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:12] local.INFO: [MatchActivityCrmData] Participants old state {"activity":612558,"participants":[{"id":999774,"user_id":206,"contact_id":null,"lead_id":null},{"id":999775,"user_id":null,"contact_id":97,"lead_id":null}]} {"correlation_id":"a105800d-d2f7-4772-a534-7791e262461c","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:12] local.INFO: [SocialAccountService] Fetching token {"socialAccountId":1499,"provider":"hubspot"} {"correlation_id":"a105800d-d2f7-4772-a534-7791e262461c","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:12] local.INFO: [SocialAccountService] Token retrieved {"socialAccountId":1499,"provider":"hubspot"} {"correlation_id":"a105800d-d2f7-4772-a534-7791e262461c","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:12] local.INFO: [EncryptedTokenManager] Generating access token. {"mode":"legacy"} {"correlation_id":"a105800d-d2f7-4772-a534-7791e262461c","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:12] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {"crm_provider":"hubspot","crm_owner":148,"team_id":2} {"correlation_id":"a105800d-d2f7-4772-a534-7791e262461c","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:12] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {"activity_id":612558,"team_id":2,"email":"[EMAIL]"} {"correlation_id":"a105800d-d2f7-4772-a534-7791e262461c","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:12] local.INFO: [Prospect match] Cache / local search hit {"identifier_type":"email","identifier":"[EMAIL]"} {"correlation_id":"a105800d-d2f7-4772-a534-7791e262461c","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:12] local.INFO: [CrmActivityService] CRM matching completed {"activity_id":612558,"participants_processed":2,"exact_matches":1,"domain_matches":0,"best_match_found":true} {"correlation_id":"a105800d-d2f7-4772-a534-7791e262461c","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:12] local.INFO: [ EsUpdateTarget ] Update single target {"target":"activities","purpose":"searchable-observer-update","entityId":612558} {"correlation_id":"a105800d-d2f7-4772-a534-7791e262461c","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:12] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {"entityType":"activities","entityId":612558,"collectionKey":"activities-for-update-priority","withPriority":true} {"correlation_id":"a105800d-d2f7-4772-a534-7791e262461c","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:12] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {"activity":612558,"remote_search":true,"lead_id":null,"contact_id":97,"account_id":69,"opportunity_id":5011,"stage_id":34} {"correlation_id":"a105800d-d2f7-4772-a534-7791e262461c","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:13] local.INFO: [MatchActivityCrmData] Starting CRM data matching {"activity":612557,"remote_search":true,"set_configuration":2,"old_state":{"lead_id":null,"contact_id":97,"account_id":69,"opportunity_id":5011,"stage_id":34}} {"correlation_id":"3ec57437-5796-4e44-8bf0-aaec149ac813","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:13] local.INFO: [ EsUpdateTarget ] Update single target {"target":"activities","purpose":"searchable-observer-update","entityId":612557} {"correlation_id":"3ec57437-5796-4e44-8bf0-aaec149ac813","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:13] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {"entityType":"activities","entityId":612557,"collectionKey":"activities-for-update-priority","withPriority":true} {"correlation_id":"3ec57437-5796-4e44-8bf0-aaec149ac813","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:13] local.INFO: [MatchActivityCrmData] Participants old state {"activity":612557,"participants":[{"id":999772,"user_id":206,"contact_id":null,"lead_id":null},{"id":999773,"user_id":null,"contact_id":97,"lead_id":null}]} {"correlation_id":"3ec57437-5796-4e44-8bf0-aaec149ac813","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:13] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"mailbox:skip-lists:refresh","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.727} {"correlation_id":"5203040f-23be-43e6-8410-ba49ee1b38b4","trace_id":"e787953d-9b78-4b63-a987-4f93f6892984"}
[2026-05-11 11:06:13] local.INFO: [SocialAccountService] Fetching token {"socialAccountId":1499,"provider":"hubspot"} {"correlation_id":"3ec57437-5796-4e44-8bf0-aaec149ac813","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:13] local.INFO: [SocialAccountService] Token retrieved {"socialAccountId":1499,"provider":"hubspot"} {"correlation_id":"3ec57437-5796-4e44-8bf0-aaec149ac813","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:13] local.INFO: [EncryptedTokenManager] Generating access token. {"mode":"legacy"} {"correlation_id":"3ec57437-5796-4e44-8bf0-aaec149ac813","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:13] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {"crm_provider":"hubspot","crm_owner":148,"team_id":2} {"correlation_id":"3ec57437-5796-4e44-8bf0-aaec149ac813","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:13] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {"activity_id":612557,"team_id":2,"email":"[EMAIL]"} {"correlation_id":"3ec57437-5796-4e44-8bf0-aaec149ac813","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:13] local.INFO: [Prospect match] Cache / local search hit {"identifier_type":"email","identifier":"[EMAIL]"} {"correlation_id":"3ec57437-5796-4e44-8bf0-aaec149ac813","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:13] local.INFO: [CrmActivityService] CRM matching completed {"activity_id":612557,"participants_processed":2,"exact_matches":1,"domain_matches":0,"best_match_found":true} {"correlation_id":"3ec57437-5796-4e44-8bf0-aaec149ac813","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:13] local.INFO: [ EsUpdateTarget ] Update single target {"target":"activities","purpose":"searchable-observer-update","entityId":612557} {"correlation_id":"3ec57437-5796-4e44-8bf0-aaec149ac813","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:13] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {"entityType":"activities","entityId":612557,"collectionKey":"activities-for-update-priority","withPriority":true} {"correlation_id":"3ec57437-5796-4e44-8bf0-aaec149ac813","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:13] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {"activity":612557,"remote_search":true,"lead_id":null,"contact_id":97,"account_id":69,"opportunity_id":5011,"stage_id":34} {"correlation_id":"3ec57437-5796-4e44-8bf0-aaec149ac813","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:13] local.INFO: Jiminny\Console\Commands\Command::run Memory usage for command {"command":"mailbox:skip-lists:refresh","memoryBeforeCommandInMb":60.0,"memoryAfterCommandInMB":60.0,"memoryPeakBeforeCommandInMb":99.727,"memoryPeakAfterCommandInMB":99.727} {"correlation_id":"5203040f-23be-43e6-8410-ba49ee1b38b4","trace_id":"e787953d-9b78-4b63-a987-4f93f6892984"}
[2026-05-11 11:06:13] local.INFO: [MatchActivityCrmData] Starting CRM data matching {"activity":612556,"remote_search":true,"set_configuration":2,"old_state":{"lead_id":null,"contact_id":97,"account_id":69,"opportunity_id":5011,"stage_id":34}} {"correlation_id":"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:13] local.INFO: [ EsUpdateTarget ] Update single target {"target":"activities","purpose":"searchable-observer-update","entityId":612556} {"correlation_id":"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:13] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {"entityType":"activities","entityId":612556,"collectionKey":"activities-for-update-priority","withPriority":true} {"correlation_id":"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:13] local.INFO: [MatchActivityCrmData] Participants old state {"activity":612556,"participants":[{"id":999770,"user_id":206,"contact_id":null,"lead_id":null},{"id":999771,"user_id":null,"contact_id":97,"lead_id":null}]} {"correlation_id":"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:13] local.INFO: [SocialAccountService] Fetching token {"socialAccountId":1499,"provider":"hubspot"} {"correlation_id":"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:13] local.INFO: [SocialAccountService] Token retrieved {"socialAccountId":1499,"provider":"hubspot"} {"correlation_id":"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:13] local.INFO: [EncryptedTokenManager] Generating access token. {"mode":"legacy"} {"correlation_id":"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:13] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {"crm_provider":"hubspot","crm_owner":148,"team_id":2} {"correlation_id":"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:14] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {"activity_id":612556,"team_id":2,"email":"[EMAIL]"} {"correlation_id":"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:14] local.INFO: [Prospect match] Cache / local search hit {"identifier_type":"email","identifier":"[EMAIL]"} {"correlation_id":"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:14] local.INFO: [CrmActivityService] CRM matching completed {"activity_id":612556,"participants_processed":2,"exact_matches":1,"domain_matches":0,"best_match_found":true} {"correlation_id":"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:14] local.INFO: [ EsUpdateTarget ] Update single target {"target":"activities","purpose":"searchable-observer-update","entityId":612556} {"correlation_id":"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:14] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {"entityType":"activities","entityId":612556,"collectionKey":"activities-for-update-priority","withPriority":true} {"correlation_id":"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:14] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {"activity":612556,"remote_search":true,"lead_id":null,"contact_id":97,"account_id":69,"opportunity_id":5011,"stage_id":34} {"correlation_id":"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:14] local.INFO: [MatchActivityCrmData] Starting CRM data matching {"activity":612555,"remote_search":true,"set_configuration":2,"old_state":{"lead_id":null,"contact_id":97,"account_id":69,"opportunity_id":5011,"stage_id":34}} {"correlation_id":"3e9d1a70-5491-44ae-8f28-5cb9b60e1859","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:14] local.INFO: [ EsUpdateTarget ] Update single target {"target":"activities","purpose":"searchable-observer-update","entityId":612555} {"correlation_id":"3e9d1a70-5491-44ae-8f28-5cb9b60e1859","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:14] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {"entityType":"activities","entityId":612555,"collectionKey":"activities-for-update-priority","withPriority":true} {"correlation_id":"3e9d1a70-5491-44ae-8f28-5cb9b60e1859","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:14] local.INFO: [MatchActivityCrmData] Participants old state {"activity":612555,"participants":[{"id":999768,"user_id":206,"contact_id":null,"lead_id":null},{"id":999769,"user_id":null,"contact_id":97,"lead_id":null}]} {"correlation_id":"3e9d1a70-5491-44ae-8f28-5cb9b60e1859","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:14] local.INFO: [SocialAccountService] Fetching token {"socialAccountId":1499,"provider":"hubspot"} {"correlation_id":"3e9d1a70-5491-44ae-8f28-5cb9b60e1859","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:14] local.INFO: [SocialAccountService] Token retrieved {"socialAccountId":1499,"provider":"hubspot"} {"correlation_id":"3e9d1a70-5491-44ae-8f28-5cb9b60e1859","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:14] local.INFO: [EncryptedTokenManager] Generating access token. {"mode":"legacy"} {"correlation_id":"3e9d1a70-5491-44ae-8f28-5cb9b60e1859","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:14] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {"crm_provider":"hubspot","crm_owner":148,"team_id":2} {"correlation_id":"3e9d1a70-5491-44ae-8f28-5cb9b60e1859","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:14] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {"activity_id":612555,"team_id":2,"email":"[EMAIL]"} {"correlation_id":"3e9d1a70-5491-44ae-8f28-5cb9b60e1859","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:14] local.INFO: [Prospect match] Cache / local search hit {"identifier_type":"email","identifier":"[EMAIL]"} {"correlation_id":"3e9d1a70-5491-44ae-8f28-5cb9b60e1859","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:14] local.INFO: [CrmActivityService] CRM matching completed {"activity_id":612555,"participants_processed":2,"exact_matches":1,"domain_matches":0,"best_match_found":true} {"correlation_id":"3e9d1a70-5491-44ae-8f28-5cb9b60e1859","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:14] local.INFO: [ EsUpdateTarget ] Update single target {"target":"activities","purpose":"searchable-observer-update","entityId":612555} {"correlation_id":"3e9d1a70-5491-44ae-8f28-5cb9b60e1859","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:14] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {"entityType":"activities","entityId":612555,"collectionKey":"activities-for-update-priority","withPriority":true} {"correlation_id":"3e9d1a70-5491-44ae-8f28-5cb9b60e1859","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:14] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {"activity":612555,"remote_search":true,"lead_id":null,"contact_id":97,"account_id":69,"opportunity_id":5011,"stage_id":34} {"correlation_id":"3e9d1a70-5491-44ae-8f28-5cb9b60e1859","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:14] local.INFO: [MatchActivityCrmData] Starting CRM data matching {"activity":612554,"remote_search":true,"set_configuration":2,"old_state":{"lead_id":null,"contact_id":97,"account_id":69,"opportunity_id":5011,"stage_id":34}} {"correlation_id":"7101e979-3a7e-4b2c-bf04-dc6a24ba1356","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:14] local.INFO: [ EsUpdateTarget ] Update single target {"target":"activities","purpose":"searchable-observer-update","entityId":612554} {"correlation_id":"7101e979-3a7e-4b2c-bf04-dc6a24ba1356","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:14] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {"entityType":"activities","entityId":612554,"collectionKey":"activities-for-update-prio...
|
18240
|
NULL
|
NULL
|
NULL
|
|
18242
|
790
|
27
|
2026-05-11T11:06:47.030845+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-11/1778 /Users/lukas/.screenpipe/data/data/2026-05-11/1778497607030_m1.jpg...
|
PhpStorm
|
faVsco.js – laravel.log
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
JY-20725-handle-HS-search Project: faVsco.js, menu
JY-20725-handle-HS-search-rate-limit, menu
Start Listening for PHP Debug Connections
HandleHubspotRateLimitTest
Run 'HandleHubspotRateLimitTest'
Debug 'HandleHubspotRateLimitTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
5
133
11
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Console\Commands;
use Carbon\Carbon;
use Carbon\CarbonImmutable;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Redis;
use InvalidArgumentException;
use Jiminny\Jobs\AutomatedReports\RequestGenerateAskJiminnyReportJob;
use Jiminny\Jobs\AutomatedReports\SendReportMailJob;
use Jiminny\Jobs\Crm\Delete\VerifyActivityCrmTaskJob;
use Jiminny\Jobs\Crm\MatchActivityCrmData;
use Jiminny\Jobs\JobDispatcherInterface;
use Jiminny\Models\Activity;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\AutomatedReportResult;
use Jiminny\Models\Team;
use Jiminny\Models\User;
use Jiminny\Repositories\AutomatedReportsRepository;
use Jiminny\Services\Activity\CrmOwnerResolver;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
/**
* Class JiminnyDebugCommand
*
* @package Jiminny\Console\Commands
*/
class JiminnyDebugCommand extends Command
{
public const string FREQUENCY_DAILY = 'daily';
public const string FREQUENCY_WEEKLY = 'weekly';
public const string FREQUENCY_MONTHLY = 'monthly';
public const string FREQUENCY_QUARTERLY = 'quarterly';
public const string FREQUENCY_ONE_OFF = 'one_off';
protected $signature = 'jiminny:debug';
public function handle(
JobDispatcherInterface $jobDispatcher,
AutomatedReportsService $automatedReportsService,
AutomatedReportsRepository $automatedReportsRepository,
UserPilotClient $userPilotClient
): void {
// Choose ONE of the following to run, then comment out the others.
// 1) Dispatch a storm of MatchActivityCrmData jobs against team 2
$this->simulateMatchActivityStorm(teamId: 2, count: 100);
// 2) Dispatch a storm of VerifyActivityCrmTaskJob jobs (simulates DeleteCrmEntityTrait fan-out)
// $this->simulateVerifyTaskStorm(teamId: 2, count: 100);
// 3) Inspect Redis circuit-breaker state for the team's HubSpot portal
// $this->observeRateLimitCache(teamId: 2);
// 4) Make 3 synchronous matchByName calls (foreground, hits API directly)
// $this->rateLimit();
exit(1);
$report = AutomatedReport::find(71);
$last = AutomatedReportResult::query()
->where('report_id', $report->getId())
->whereIn('status', [AutomatedReportResult::STATUS_DEFAULT, AutomatedReportResult::STATUS_FAILED])
// ->where('reason', '!=', AutomatedReportResult::REASON_NOT_ENOUGH_ACTIVITIES)
->whereDate('created_at', CarbonImmutable::now()->toDateString())
->latest()
->first();
$this->info("Last: {$last->getId()}");
exit(1);
$user = User::find(143);
// $count = $automatedReportsRepository->countUserReports($user);
// $this->info("Count: {$count}");
// $count = $automatedReportsRepository->countAllUserReports($user);
// $this->info("All count: {$count}");
$payload = [
'report_type' => 'ask_jiminny',
'frequency' => 'weekly',
];
$userPilotClient->track($user, 'ask-jiminny-report-generated', $payload);
exit(1);
$now = Carbon::now()->subDay(1);
$this->info("Now: {$now->toDateTimeString()}");
$weekStart = Carbon::getWeekStartsAt();
$this->info("Now: {$weekStart}");
// $from = $now->copy()->previousWeekday()->startOfDay();
// $to = $now->copy()->previousWeekday()->endOfDay();
// $fromOld = $now->copy()->subWeeks(1)->startOfDay();
// $toOld = $now->copy()->subDay()->endOfDay();
// $fromNew = $now->copy()->subWeek()->startOfWeek();
// $toNew = $now->copy()->subWeek()->endOfWeek();
// $fromOld = $now->copy()->subMonths(1)->startOfDay();
// $toOld = $now->copy()->subDay()->endOfDay();
// $fromNew = $now->copy()->subMonthNoOverflow()->startOfMonth();
// $toNew = $now->copy()->subMonthNoOverflow()->endOfMonth();
$fromOld = $now->copy()->subMonths(3)->startOfDay();
$toOld = $now->copy()->subDay()->endOfDay();
$fromNew = $now->copy()->subQuarterNoOverflow()->startOfQuarter();
$toNew = $now->copy()->subQuarterNoOverflow()->endOfQuarter();
$this->info("From old: {$fromOld->toDateTimeString()}");
$this->info("To old: {$toOld->toDateTimeString()}");
$this->info("From new: {$fromNew->toDateTimeString()}");
$this->info("To new: {$toNew->toDateTimeString()}");
exit(1);
$report = AutomatedReport::find(71);
$job = new RequestGenerateAskJiminnyReportJob($report->getUuid());
$jobDispatcher->dispatch($job);
exit(1);
// $this->formatDate($jobDispatcher);
// $this->sendMail($jobDispatcher, $automatedReportsService);
// $this->crmService();
$this->getPayload($automatedReportsService);
exit(1);
}
private function crmService()
{
$activity = Activity::find(418141);
$team = Team::find(19);
$config = $team->getCrmConfiguration();
$crmResolver = app(CrmOwnerResolver::class, [
'team' => $team,
'integrationAdmin' => $team->getOwner(),
'providerSlug' => $config->getProviderName(),
]);
$crmService = $crmResolver->prepareCrmService();
$crmService->createTranscriptNotes($activity);
}
private function sendMail(JobDispatcherInterface $jobDispatcher, AutomatedReportsService $automatedReportsService)
{
$reportUuid = '';
// $report = $automatedReportsService->getReportResult($reportUuid);
$report = AutomatedReportResult::find(275);
$validRecipients = $automatedReportsService->getValidRecipientUsers(
$report->getReport(),
includeJiminny: true,
);
$recipient = $validRecipients[0];
$fileName = $automatedReportsService->getReportFileName($report);
$typeName = $report->getReport()->getCustomName()
?? $automatedReportsService->getReportTypeName($report);
$teamsName = $automatedReportsService->getReportTeamsName($report);
$periodName = $automatedReportsService->getReportPeriodName($report);
$s3Path = $automatedReportsService->getMediaPath($report);
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$fileName ' . PHP_EOL . print_r($fileName, true));
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$typeName ' . PHP_EOL . print_r($typeName, true));
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$teamsName ' . PHP_EOL . print_r($teamsName, true));
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$periodName ' . PHP_EOL . print_r($periodName, true));
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$s3Path ' . PHP_EOL . print_r($s3Path, true));
$jobDispatcher->dispatch(
new SendReportMailJob(
reportUuid: $report->getUuid(),
s3Path: $s3Path,
recipientEmail: $recipient['email'],
recipientName: $recipient['name'] ?? null,
fileName: $fileName,
typeName: $typeName,
teamsName: $teamsName,
periodName: $periodName,
isAskJiminny: true,
)
);
exit(1);
}
private function formatDate(JobDispatcherInterface $jobDispatcher): void
{
$customName = 'Custom report name';
// $frequency = self::FREQUENCY_DAILY;
// $frequency = self::FREQUENCY_WEEKLY;
$frequency = self::FREQUENCY_MONTHLY;
// $frequency = self::FREQUENCY_QUARTERLY;
// $frequency = self::FREQUENCY_ONE_OFF;
$period = $this->calculateFromAndToDatePeriod($frequency);
$from = $period['fromDate'];
$to = $period['toDate'];
$periodName = $this->formatReportPeriodName($frequency, $from, $to);
$filenameSuffix = null;
if ($customName) {
if ($filenameSuffix) {
$customName .= " {$filenameSuffix}";
}
$result = $this->sanitizeFileName("{$customName} - {$periodName}");
}
$this->info($result);
}
public function calculateFromAndToDatePeriod(
string $frequency,
?Carbon $fromDate = null,
?Carbon $toDate = null
): array {
if ($frequency === self::FREQUENCY_ONE_OFF) {
return [
'fromDate' => $fromDate,
'toDate' => $toDate,
];
}
$now = Carbon::now();
return match ($frequency) {
self::FREQUENCY_DAILY => [
'fromDate' => $now->copy()->subDay()->startOfDay(),
'toDate' => $now->copy()->subDay()->endOfDay(),
],
self::FREQUENCY_WEEKLY => [
'fromDate' => $now->copy()->subWeeks(1)->startOfDay(),
'toDate' => $now->copy()->subDay()->endOfDay(),
],
self::FREQUENCY_MONTHLY => [
'fromDate' => $now->copy()->subMonths(1)->startOfDay(),
'toDate' => $now->copy()->subDay()->endOfDay(),
],
self::FREQUENCY_QUARTERLY => [
'fromDate' => $now->copy()->subMonths(3)->startOfDay(),
'toDate' => $now->copy()->subDay()->endOfDay(),
],
default => throw new InvalidArgumentException("Unsupported frequency: {$frequency}"),
};
}
private function formatReportPeriodName(string $frequency, Carbon $from, Carbon $to): string
{
$fromYear = $from->format('Y');
$toYear = $to->format('Y');
$differentYears = $fromYear !== $toYear;
switch ($frequency) {
case self::FREQUENCY_DAILY:
return $from->format('j M Y');
case self::FREQUENCY_QUARTERLY:
// 'Jan-Mar 2025' or 'Nov 2024-Jan 2025' if years differ
$startMonth = $from->format('M');
$endMonth = $to->copy()->subMonth();
$endMonthName = $endMonth->format('M');
$endMonthYear = $endMonth->format('Y');
if ($differentYears) {
return "{$startMonth} {$fromYear} - {$endMonthName} {$endMonthYear}";
}
return "{$startMonth} - {$endMonthName} {$toYear}";
case self::FREQUENCY_MONTHLY:
// 'May 2025' - monthly reports are always within the same year
return $from->format('M Y');
case self::FREQUENCY_WEEKLY:
// '4 - 8 Aug 2025', '27 Oct - 3 Nov 2025', or '28 Dec 2024 - 3 Jan 2025' if years differ
$startDay = $from->format('j');
$endDay = $to->format('j');
$startMonth = $from->format('M');
$endMonth = $to->format('M');
if ($differentYears) {
return "{$startDay} {$startMonth} {$fromYear} - {$endDay} {$endMonth} {$toYear}";
}
if ($startMonth !== $endMonth) {
return "{$startDay} {$startMonth} - {$endDay} {$endMonth} {$toYear}";
}
return "{$startDay} - {$endDay} {$endMonth} {$toYear}";
case self::FREQUENCY_ONE_OFF:
// '2 May-31 May 2025' or '15 Dec 2024-15 Jan 2025' if years differ
$startDay = $from->format('j');
$startMonth = $from->format('M');
$endDay = $to->format('j');
$endMonth = $to->format('M');
// If same month and year, use a format like '2-31 May 2025'
if ($startMonth === $endMonth && ! $differentYears) {
return "{$startDay} - {$endDay} {$startMonth} {$toYear}";
}
// If different years, include both years
if ($differentYears) {
return "{$startDay} {$startMonth} {$fromYear} - {$endDay} {$endMonth} {$toYear}";
}
// Same year but different months
return "{$startDay} {$startMonth} - {$endDay} {$endMonth} {$toYear}";
default:
// Default format for unknown frequencies
return $from->format('j M Y') . ' - ' . $to->format('j M Y');
}
}
public function sanitizeFileName(string $fileName): string
{
return str_replace(['/', '\\'], '-', $fileName);
}
private function getPayload(AutomatedReportsService $automatedReportsService)
{
$reportResult = AutomatedReportResult::find(269);
$automatedReport = $reportResult->getReport();
$activityIds = [1,2,3];
$payload = $automatedReportsService->getAskJiminnyGenerateReportPayload(
automatedReport: $automatedReport,
reportResult: $reportResult,
activityIds: $activityIds,
);
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$payload ' . PHP_EOL . print_r($payload, true));
}
private function rateLimit()
{
$team = Team::find(2);
$config = $team->getCrmConfiguration();
$crmResolver = app(CrmOwnerResolver::class, [
'team' => $team,
'integrationAdmin' => $team->getOwner(),
'providerSlug' => $config->getProviderName(),
]);
$crmService = $crmResolver->prepareCrmService();
for ($i = 0 ; $i < 3; $i++) {
// if ($i % 25 === 0) {
// $this->info("Syncing opportunity {$i}");
$this->info("Matching contact {$i}");
// }
// $crmService->syncOpportunity('374720564');
$crmService->matchByName('Robot');
}
}
private function simulateMatchActivityStorm(int $teamId = 2, int $count = 100): void
{
$team = Team::find($teamId);
$config = $team->getCrmConfiguration();
$activities = Activity::query()
->where('crm_configuration_id', $config->getId())
->orderByDesc('id')
->limit($count)
->get();
$this->info("Dispatching {$activities->count()} MatchActivityCrmData jobs (portal={$config->getId()})");
foreach ($activities as $activity) {
MatchActivityCrmData::dispatch($activity->getId(), $config, true);
}
$this->info('Done. Watch logs and run jiminny:debug observeRateLimit to inspect cache state.');
}
private function simulateVerifyTaskStorm(int $teamId = 2, int $count = 100): void
{
$activities = Activity::query()
->where('team_id', $teamId)
->whereNotNull('crm_provider_id')
->orderByDesc('id')
->limit($count)
->get();
$this->info("Dispatching {$activities->count()} VerifyActivityCrmTaskJob jobs");
foreach ($activities as $activity) {
VerifyActivityCrmTaskJob::dispatch($activity->getId());
}
$this->info('Done.');
}
private function observeRateLimitCache(int $teamId = 2): void
{
$team = Team::find($teamId);
$config = $team->getCrmConfiguration();
$key = sprintf('hubspot:ratelimit:portal:%d', $config->getId());
$value = Redis::get($key);
$ttl = Redis::ttl($key);
$this->info("Redis key: {$key}");
$this->info('Value: ' . ($value ?? '(empty)'));
$this->info("TTL: {$ttl}s");
}
}
Show Replace Field
Search History
429
New Line
Match Case
Words
Regex
Replace History
Replace
New Line
Preserve case
0 results
Previous Occurrence
Next Occurrence
Filter Search Results
Open in Window, Multiple Cursors
Click to highlight...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JY-20725-handle-HS-search-rate-limit, menu","depth":5,"on_screen":true,"help_text":"Git Branch: JY-20725-handle-HS-search-rate-limit","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"HandleHubspotRateLimitTest","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'HandleHubspotRateLimitTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'HandleHubspotRateLimitTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"5","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"133","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"11","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Console\\Commands;\n\nuse Carbon\\Carbon;\nuse Carbon\\CarbonImmutable;\nuse Illuminate\\Console\\Command;\nuse Illuminate\\Support\\Facades\\Redis;\nuse InvalidArgumentException;\nuse Jiminny\\Jobs\\AutomatedReports\\RequestGenerateAskJiminnyReportJob;\nuse Jiminny\\Jobs\\AutomatedReports\\SendReportMailJob;\nuse Jiminny\\Jobs\\Crm\\Delete\\VerifyActivityCrmTaskJob;\nuse Jiminny\\Jobs\\Crm\\MatchActivityCrmData;\nuse Jiminny\\Jobs\\JobDispatcherInterface;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\AutomatedReportResult;\nuse Jiminny\\Models\\Team;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Repositories\\AutomatedReportsRepository;\nuse Jiminny\\Services\\Activity\\CrmOwnerResolver;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\n\n/**\n * Class JiminnyDebugCommand\n *\n * @package Jiminny\\Console\\Commands\n */\nclass JiminnyDebugCommand extends Command\n{\n public const string FREQUENCY_DAILY = 'daily';\n public const string FREQUENCY_WEEKLY = 'weekly';\n public const string FREQUENCY_MONTHLY = 'monthly';\n public const string FREQUENCY_QUARTERLY = 'quarterly';\n public const string FREQUENCY_ONE_OFF = 'one_off';\n protected $signature = 'jiminny:debug';\n\n public function handle(\n JobDispatcherInterface $jobDispatcher,\n AutomatedReportsService $automatedReportsService,\n AutomatedReportsRepository $automatedReportsRepository,\n UserPilotClient $userPilotClient\n ): void {\n // Choose ONE of the following to run, then comment out the others.\n // 1) Dispatch a storm of MatchActivityCrmData jobs against team 2\n $this->simulateMatchActivityStorm(teamId: 2, count: 100);\n\n // 2) Dispatch a storm of VerifyActivityCrmTaskJob jobs (simulates DeleteCrmEntityTrait fan-out)\n // $this->simulateVerifyTaskStorm(teamId: 2, count: 100);\n\n // 3) Inspect Redis circuit-breaker state for the team's HubSpot portal\n // $this->observeRateLimitCache(teamId: 2);\n\n // 4) Make 3 synchronous matchByName calls (foreground, hits API directly)\n // $this->rateLimit();\n exit(1);\n\n\n\n $report = AutomatedReport::find(71);\n $last = AutomatedReportResult::query()\n ->where('report_id', $report->getId())\n ->whereIn('status', [AutomatedReportResult::STATUS_DEFAULT, AutomatedReportResult::STATUS_FAILED])\n// ->where('reason', '!=', AutomatedReportResult::REASON_NOT_ENOUGH_ACTIVITIES)\n ->whereDate('created_at', CarbonImmutable::now()->toDateString())\n ->latest()\n ->first();\n\n $this->info(\"Last: {$last->getId()}\");\n\n exit(1);\n\n $user = User::find(143);\n // $count = $automatedReportsRepository->countUserReports($user);\n // $this->info(\"Count: {$count}\");\n // $count = $automatedReportsRepository->countAllUserReports($user);\n // $this->info(\"All count: {$count}\");\n\n $payload = [\n 'report_type' => 'ask_jiminny',\n 'frequency' => 'weekly',\n ];\n $userPilotClient->track($user, 'ask-jiminny-report-generated', $payload);\n\n exit(1);\n\n $now = Carbon::now()->subDay(1);\n $this->info(\"Now: {$now->toDateTimeString()}\");\n $weekStart = Carbon::getWeekStartsAt();\n $this->info(\"Now: {$weekStart}\");\n\n // $from = $now->copy()->previousWeekday()->startOfDay();\n // $to = $now->copy()->previousWeekday()->endOfDay();\n\n // $fromOld = $now->copy()->subWeeks(1)->startOfDay();\n // $toOld = $now->copy()->subDay()->endOfDay();\n // $fromNew = $now->copy()->subWeek()->startOfWeek();\n // $toNew = $now->copy()->subWeek()->endOfWeek();\n\n // $fromOld = $now->copy()->subMonths(1)->startOfDay();\n // $toOld = $now->copy()->subDay()->endOfDay();\n // $fromNew = $now->copy()->subMonthNoOverflow()->startOfMonth();\n // $toNew = $now->copy()->subMonthNoOverflow()->endOfMonth();\n\n $fromOld = $now->copy()->subMonths(3)->startOfDay();\n $toOld = $now->copy()->subDay()->endOfDay();\n $fromNew = $now->copy()->subQuarterNoOverflow()->startOfQuarter();\n $toNew = $now->copy()->subQuarterNoOverflow()->endOfQuarter();\n\n $this->info(\"From old: {$fromOld->toDateTimeString()}\");\n $this->info(\"To old: {$toOld->toDateTimeString()}\");\n $this->info(\"From new: {$fromNew->toDateTimeString()}\");\n $this->info(\"To new: {$toNew->toDateTimeString()}\");\n\n exit(1);\n\n $report = AutomatedReport::find(71);\n\n $job = new RequestGenerateAskJiminnyReportJob($report->getUuid());\n $jobDispatcher->dispatch($job);\n\n exit(1);\n\n\n // $this->formatDate($jobDispatcher);\n // $this->sendMail($jobDispatcher, $automatedReportsService);\n // $this->crmService();\n\n $this->getPayload($automatedReportsService);\n\n exit(1);\n }\n\n\n\n private function crmService()\n {\n $activity = Activity::find(418141);\n\n $team = Team::find(19);\n $config = $team->getCrmConfiguration();\n\n $crmResolver = app(CrmOwnerResolver::class, [\n 'team' => $team,\n 'integrationAdmin' => $team->getOwner(),\n 'providerSlug' => $config->getProviderName(),\n ]);\n\n $crmService = $crmResolver->prepareCrmService();\n\n $crmService->createTranscriptNotes($activity);\n }\n\n private function sendMail(JobDispatcherInterface $jobDispatcher, AutomatedReportsService $automatedReportsService)\n {\n $reportUuid = '';\n // $report = $automatedReportsService->getReportResult($reportUuid);\n $report = AutomatedReportResult::find(275);\n $validRecipients = $automatedReportsService->getValidRecipientUsers(\n $report->getReport(),\n includeJiminny: true,\n );\n\n $recipient = $validRecipients[0];\n\n $fileName = $automatedReportsService->getReportFileName($report);\n $typeName = $report->getReport()->getCustomName()\n ?? $automatedReportsService->getReportTypeName($report);\n $teamsName = $automatedReportsService->getReportTeamsName($report);\n $periodName = $automatedReportsService->getReportPeriodName($report);\n $s3Path = $automatedReportsService->getMediaPath($report);\n\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$fileName ' . PHP_EOL . print_r($fileName, true));\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$typeName ' . PHP_EOL . print_r($typeName, true));\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$teamsName ' . PHP_EOL . print_r($teamsName, true));\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$periodName ' . PHP_EOL . print_r($periodName, true));\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$s3Path ' . PHP_EOL . print_r($s3Path, true));\n\n $jobDispatcher->dispatch(\n new SendReportMailJob(\n reportUuid: $report->getUuid(),\n s3Path: $s3Path,\n recipientEmail: $recipient['email'],\n recipientName: $recipient['name'] ?? null,\n fileName: $fileName,\n typeName: $typeName,\n teamsName: $teamsName,\n periodName: $periodName,\n isAskJiminny: true,\n )\n );\n\n exit(1);\n }\n\n private function formatDate(JobDispatcherInterface $jobDispatcher): void\n {\n $customName = 'Custom report name';\n // $frequency = self::FREQUENCY_DAILY;\n // $frequency = self::FREQUENCY_WEEKLY;\n $frequency = self::FREQUENCY_MONTHLY;\n // $frequency = self::FREQUENCY_QUARTERLY;\n // $frequency = self::FREQUENCY_ONE_OFF;\n $period = $this->calculateFromAndToDatePeriod($frequency);\n $from = $period['fromDate'];\n $to = $period['toDate'];\n $periodName = $this->formatReportPeriodName($frequency, $from, $to);\n $filenameSuffix = null;\n\n if ($customName) {\n if ($filenameSuffix) {\n $customName .= \" {$filenameSuffix}\";\n }\n\n $result = $this->sanitizeFileName(\"{$customName} - {$periodName}\");\n }\n\n $this->info($result);\n }\n\n public function calculateFromAndToDatePeriod(\n string $frequency,\n ?Carbon $fromDate = null,\n ?Carbon $toDate = null\n ): array {\n if ($frequency === self::FREQUENCY_ONE_OFF) {\n return [\n 'fromDate' => $fromDate,\n 'toDate' => $toDate,\n ];\n }\n\n $now = Carbon::now();\n\n return match ($frequency) {\n self::FREQUENCY_DAILY => [\n 'fromDate' => $now->copy()->subDay()->startOfDay(),\n 'toDate' => $now->copy()->subDay()->endOfDay(),\n ],\n self::FREQUENCY_WEEKLY => [\n 'fromDate' => $now->copy()->subWeeks(1)->startOfDay(),\n 'toDate' => $now->copy()->subDay()->endOfDay(),\n ],\n self::FREQUENCY_MONTHLY => [\n 'fromDate' => $now->copy()->subMonths(1)->startOfDay(),\n 'toDate' => $now->copy()->subDay()->endOfDay(),\n ],\n self::FREQUENCY_QUARTERLY => [\n 'fromDate' => $now->copy()->subMonths(3)->startOfDay(),\n 'toDate' => $now->copy()->subDay()->endOfDay(),\n ],\n default => throw new InvalidArgumentException(\"Unsupported frequency: {$frequency}\"),\n };\n }\n\n private function formatReportPeriodName(string $frequency, Carbon $from, Carbon $to): string\n {\n $fromYear = $from->format('Y');\n $toYear = $to->format('Y');\n $differentYears = $fromYear !== $toYear;\n\n switch ($frequency) {\n case self::FREQUENCY_DAILY:\n return $from->format('j M Y');\n\n case self::FREQUENCY_QUARTERLY:\n // 'Jan-Mar 2025' or 'Nov 2024-Jan 2025' if years differ\n $startMonth = $from->format('M');\n $endMonth = $to->copy()->subMonth();\n $endMonthName = $endMonth->format('M');\n $endMonthYear = $endMonth->format('Y');\n\n if ($differentYears) {\n return \"{$startMonth} {$fromYear} - {$endMonthName} {$endMonthYear}\";\n }\n\n return \"{$startMonth} - {$endMonthName} {$toYear}\";\n\n case self::FREQUENCY_MONTHLY:\n // 'May 2025' - monthly reports are always within the same year\n return $from->format('M Y');\n\n case self::FREQUENCY_WEEKLY:\n // '4 - 8 Aug 2025', '27 Oct - 3 Nov 2025', or '28 Dec 2024 - 3 Jan 2025' if years differ\n $startDay = $from->format('j');\n $endDay = $to->format('j');\n $startMonth = $from->format('M');\n $endMonth = $to->format('M');\n\n if ($differentYears) {\n return \"{$startDay} {$startMonth} {$fromYear} - {$endDay} {$endMonth} {$toYear}\";\n }\n\n if ($startMonth !== $endMonth) {\n return \"{$startDay} {$startMonth} - {$endDay} {$endMonth} {$toYear}\";\n }\n\n return \"{$startDay} - {$endDay} {$endMonth} {$toYear}\";\n\n case self::FREQUENCY_ONE_OFF:\n // '2 May-31 May 2025' or '15 Dec 2024-15 Jan 2025' if years differ\n $startDay = $from->format('j');\n $startMonth = $from->format('M');\n $endDay = $to->format('j');\n $endMonth = $to->format('M');\n\n // If same month and year, use a format like '2-31 May 2025'\n if ($startMonth === $endMonth && ! $differentYears) {\n return \"{$startDay} - {$endDay} {$startMonth} {$toYear}\";\n }\n\n // If different years, include both years\n if ($differentYears) {\n return \"{$startDay} {$startMonth} {$fromYear} - {$endDay} {$endMonth} {$toYear}\";\n }\n\n // Same year but different months\n return \"{$startDay} {$startMonth} - {$endDay} {$endMonth} {$toYear}\";\n\n default:\n // Default format for unknown frequencies\n return $from->format('j M Y') . ' - ' . $to->format('j M Y');\n }\n }\n\n public function sanitizeFileName(string $fileName): string\n {\n return str_replace(['/', '\\\\'], '-', $fileName);\n }\n\n private function getPayload(AutomatedReportsService $automatedReportsService)\n {\n $reportResult = AutomatedReportResult::find(269);\n $automatedReport = $reportResult->getReport();\n $activityIds = [1,2,3];\n $payload = $automatedReportsService->getAskJiminnyGenerateReportPayload(\n automatedReport: $automatedReport,\n reportResult: $reportResult,\n activityIds: $activityIds,\n );\n\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$payload ' . PHP_EOL . print_r($payload, true));\n }\n\n private function rateLimit()\n {\n $team = Team::find(2);\n $config = $team->getCrmConfiguration();\n\n $crmResolver = app(CrmOwnerResolver::class, [\n 'team' => $team,\n 'integrationAdmin' => $team->getOwner(),\n 'providerSlug' => $config->getProviderName(),\n ]);\n\n $crmService = $crmResolver->prepareCrmService();\n\n for ($i = 0 ; $i < 3; $i++) {\n// if ($i % 25 === 0) {\n// $this->info(\"Syncing opportunity {$i}\");\n $this->info(\"Matching contact {$i}\");\n// }\n// $crmService->syncOpportunity('374720564');\n $crmService->matchByName('Robot');\n }\n }\n\n private function simulateMatchActivityStorm(int $teamId = 2, int $count = 100): void\n {\n $team = Team::find($teamId);\n $config = $team->getCrmConfiguration();\n\n $activities = Activity::query()\n ->where('crm_configuration_id', $config->getId())\n ->orderByDesc('id')\n ->limit($count)\n ->get();\n\n $this->info(\"Dispatching {$activities->count()} MatchActivityCrmData jobs (portal={$config->getId()})\");\n\n foreach ($activities as $activity) {\n MatchActivityCrmData::dispatch($activity->getId(), $config, true);\n }\n\n $this->info('Done. Watch logs and run jiminny:debug observeRateLimit to inspect cache state.');\n }\n\n private function simulateVerifyTaskStorm(int $teamId = 2, int $count = 100): void\n {\n $activities = Activity::query()\n ->where('team_id', $teamId)\n ->whereNotNull('crm_provider_id')\n ->orderByDesc('id')\n ->limit($count)\n ->get();\n\n $this->info(\"Dispatching {$activities->count()} VerifyActivityCrmTaskJob jobs\");\n\n foreach ($activities as $activity) {\n VerifyActivityCrmTaskJob::dispatch($activity->getId());\n }\n\n $this->info('Done.');\n }\n\n private function observeRateLimitCache(int $teamId = 2): void\n {\n $team = Team::find($teamId);\n $config = $team->getCrmConfiguration();\n $key = sprintf('hubspot:ratelimit:portal:%d', $config->getId());\n\n $value = Redis::get($key);\n $ttl = Redis::ttl($key);\n\n $this->info(\"Redis key: {$key}\");\n $this->info('Value: ' . ($value ?? '(empty)'));\n $this->info(\"TTL: {$ttl}s\");\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Console\\Commands;\n\nuse Carbon\\Carbon;\nuse Carbon\\CarbonImmutable;\nuse Illuminate\\Console\\Command;\nuse Illuminate\\Support\\Facades\\Redis;\nuse InvalidArgumentException;\nuse Jiminny\\Jobs\\AutomatedReports\\RequestGenerateAskJiminnyReportJob;\nuse Jiminny\\Jobs\\AutomatedReports\\SendReportMailJob;\nuse Jiminny\\Jobs\\Crm\\Delete\\VerifyActivityCrmTaskJob;\nuse Jiminny\\Jobs\\Crm\\MatchActivityCrmData;\nuse Jiminny\\Jobs\\JobDispatcherInterface;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\AutomatedReportResult;\nuse Jiminny\\Models\\Team;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Repositories\\AutomatedReportsRepository;\nuse Jiminny\\Services\\Activity\\CrmOwnerResolver;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\n\n/**\n * Class JiminnyDebugCommand\n *\n * @package Jiminny\\Console\\Commands\n */\nclass JiminnyDebugCommand extends Command\n{\n public const string FREQUENCY_DAILY = 'daily';\n public const string FREQUENCY_WEEKLY = 'weekly';\n public const string FREQUENCY_MONTHLY = 'monthly';\n public const string FREQUENCY_QUARTERLY = 'quarterly';\n public const string FREQUENCY_ONE_OFF = 'one_off';\n protected $signature = 'jiminny:debug';\n\n public function handle(\n JobDispatcherInterface $jobDispatcher,\n AutomatedReportsService $automatedReportsService,\n AutomatedReportsRepository $automatedReportsRepository,\n UserPilotClient $userPilotClient\n ): void {\n // Choose ONE of the following to run, then comment out the others.\n // 1) Dispatch a storm of MatchActivityCrmData jobs against team 2\n $this->simulateMatchActivityStorm(teamId: 2, count: 100);\n\n // 2) Dispatch a storm of VerifyActivityCrmTaskJob jobs (simulates DeleteCrmEntityTrait fan-out)\n // $this->simulateVerifyTaskStorm(teamId: 2, count: 100);\n\n // 3) Inspect Redis circuit-breaker state for the team's HubSpot portal\n // $this->observeRateLimitCache(teamId: 2);\n\n // 4) Make 3 synchronous matchByName calls (foreground, hits API directly)\n // $this->rateLimit();\n exit(1);\n\n\n\n $report = AutomatedReport::find(71);\n $last = AutomatedReportResult::query()\n ->where('report_id', $report->getId())\n ->whereIn('status', [AutomatedReportResult::STATUS_DEFAULT, AutomatedReportResult::STATUS_FAILED])\n// ->where('reason', '!=', AutomatedReportResult::REASON_NOT_ENOUGH_ACTIVITIES)\n ->whereDate('created_at', CarbonImmutable::now()->toDateString())\n ->latest()\n ->first();\n\n $this->info(\"Last: {$last->getId()}\");\n\n exit(1);\n\n $user = User::find(143);\n // $count = $automatedReportsRepository->countUserReports($user);\n // $this->info(\"Count: {$count}\");\n // $count = $automatedReportsRepository->countAllUserReports($user);\n // $this->info(\"All count: {$count}\");\n\n $payload = [\n 'report_type' => 'ask_jiminny',\n 'frequency' => 'weekly',\n ];\n $userPilotClient->track($user, 'ask-jiminny-report-generated', $payload);\n\n exit(1);\n\n $now = Carbon::now()->subDay(1);\n $this->info(\"Now: {$now->toDateTimeString()}\");\n $weekStart = Carbon::getWeekStartsAt();\n $this->info(\"Now: {$weekStart}\");\n\n // $from = $now->copy()->previousWeekday()->startOfDay();\n // $to = $now->copy()->previousWeekday()->endOfDay();\n\n // $fromOld = $now->copy()->subWeeks(1)->startOfDay();\n // $toOld = $now->copy()->subDay()->endOfDay();\n // $fromNew = $now->copy()->subWeek()->startOfWeek();\n // $toNew = $now->copy()->subWeek()->endOfWeek();\n\n // $fromOld = $now->copy()->subMonths(1)->startOfDay();\n // $toOld = $now->copy()->subDay()->endOfDay();\n // $fromNew = $now->copy()->subMonthNoOverflow()->startOfMonth();\n // $toNew = $now->copy()->subMonthNoOverflow()->endOfMonth();\n\n $fromOld = $now->copy()->subMonths(3)->startOfDay();\n $toOld = $now->copy()->subDay()->endOfDay();\n $fromNew = $now->copy()->subQuarterNoOverflow()->startOfQuarter();\n $toNew = $now->copy()->subQuarterNoOverflow()->endOfQuarter();\n\n $this->info(\"From old: {$fromOld->toDateTimeString()}\");\n $this->info(\"To old: {$toOld->toDateTimeString()}\");\n $this->info(\"From new: {$fromNew->toDateTimeString()}\");\n $this->info(\"To new: {$toNew->toDateTimeString()}\");\n\n exit(1);\n\n $report = AutomatedReport::find(71);\n\n $job = new RequestGenerateAskJiminnyReportJob($report->getUuid());\n $jobDispatcher->dispatch($job);\n\n exit(1);\n\n\n // $this->formatDate($jobDispatcher);\n // $this->sendMail($jobDispatcher, $automatedReportsService);\n // $this->crmService();\n\n $this->getPayload($automatedReportsService);\n\n exit(1);\n }\n\n\n\n private function crmService()\n {\n $activity = Activity::find(418141);\n\n $team = Team::find(19);\n $config = $team->getCrmConfiguration();\n\n $crmResolver = app(CrmOwnerResolver::class, [\n 'team' => $team,\n 'integrationAdmin' => $team->getOwner(),\n 'providerSlug' => $config->getProviderName(),\n ]);\n\n $crmService = $crmResolver->prepareCrmService();\n\n $crmService->createTranscriptNotes($activity);\n }\n\n private function sendMail(JobDispatcherInterface $jobDispatcher, AutomatedReportsService $automatedReportsService)\n {\n $reportUuid = '';\n // $report = $automatedReportsService->getReportResult($reportUuid);\n $report = AutomatedReportResult::find(275);\n $validRecipients = $automatedReportsService->getValidRecipientUsers(\n $report->getReport(),\n includeJiminny: true,\n );\n\n $recipient = $validRecipients[0];\n\n $fileName = $automatedReportsService->getReportFileName($report);\n $typeName = $report->getReport()->getCustomName()\n ?? $automatedReportsService->getReportTypeName($report);\n $teamsName = $automatedReportsService->getReportTeamsName($report);\n $periodName = $automatedReportsService->getReportPeriodName($report);\n $s3Path = $automatedReportsService->getMediaPath($report);\n\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$fileName ' . PHP_EOL . print_r($fileName, true));\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$typeName ' . PHP_EOL . print_r($typeName, true));\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$teamsName ' . PHP_EOL . print_r($teamsName, true));\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$periodName ' . PHP_EOL . print_r($periodName, true));\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$s3Path ' . PHP_EOL . print_r($s3Path, true));\n\n $jobDispatcher->dispatch(\n new SendReportMailJob(\n reportUuid: $report->getUuid(),\n s3Path: $s3Path,\n recipientEmail: $recipient['email'],\n recipientName: $recipient['name'] ?? null,\n fileName: $fileName,\n typeName: $typeName,\n teamsName: $teamsName,\n periodName: $periodName,\n isAskJiminny: true,\n )\n );\n\n exit(1);\n }\n\n private function formatDate(JobDispatcherInterface $jobDispatcher): void\n {\n $customName = 'Custom report name';\n // $frequency = self::FREQUENCY_DAILY;\n // $frequency = self::FREQUENCY_WEEKLY;\n $frequency = self::FREQUENCY_MONTHLY;\n // $frequency = self::FREQUENCY_QUARTERLY;\n // $frequency = self::FREQUENCY_ONE_OFF;\n $period = $this->calculateFromAndToDatePeriod($frequency);\n $from = $period['fromDate'];\n $to = $period['toDate'];\n $periodName = $this->formatReportPeriodName($frequency, $from, $to);\n $filenameSuffix = null;\n\n if ($customName) {\n if ($filenameSuffix) {\n $customName .= \" {$filenameSuffix}\";\n }\n\n $result = $this->sanitizeFileName(\"{$customName} - {$periodName}\");\n }\n\n $this->info($result);\n }\n\n public function calculateFromAndToDatePeriod(\n string $frequency,\n ?Carbon $fromDate = null,\n ?Carbon $toDate = null\n ): array {\n if ($frequency === self::FREQUENCY_ONE_OFF) {\n return [\n 'fromDate' => $fromDate,\n 'toDate' => $toDate,\n ];\n }\n\n $now = Carbon::now();\n\n return match ($frequency) {\n self::FREQUENCY_DAILY => [\n 'fromDate' => $now->copy()->subDay()->startOfDay(),\n 'toDate' => $now->copy()->subDay()->endOfDay(),\n ],\n self::FREQUENCY_WEEKLY => [\n 'fromDate' => $now->copy()->subWeeks(1)->startOfDay(),\n 'toDate' => $now->copy()->subDay()->endOfDay(),\n ],\n self::FREQUENCY_MONTHLY => [\n 'fromDate' => $now->copy()->subMonths(1)->startOfDay(),\n 'toDate' => $now->copy()->subDay()->endOfDay(),\n ],\n self::FREQUENCY_QUARTERLY => [\n 'fromDate' => $now->copy()->subMonths(3)->startOfDay(),\n 'toDate' => $now->copy()->subDay()->endOfDay(),\n ],\n default => throw new InvalidArgumentException(\"Unsupported frequency: {$frequency}\"),\n };\n }\n\n private function formatReportPeriodName(string $frequency, Carbon $from, Carbon $to): string\n {\n $fromYear = $from->format('Y');\n $toYear = $to->format('Y');\n $differentYears = $fromYear !== $toYear;\n\n switch ($frequency) {\n case self::FREQUENCY_DAILY:\n return $from->format('j M Y');\n\n case self::FREQUENCY_QUARTERLY:\n // 'Jan-Mar 2025' or 'Nov 2024-Jan 2025' if years differ\n $startMonth = $from->format('M');\n $endMonth = $to->copy()->subMonth();\n $endMonthName = $endMonth->format('M');\n $endMonthYear = $endMonth->format('Y');\n\n if ($differentYears) {\n return \"{$startMonth} {$fromYear} - {$endMonthName} {$endMonthYear}\";\n }\n\n return \"{$startMonth} - {$endMonthName} {$toYear}\";\n\n case self::FREQUENCY_MONTHLY:\n // 'May 2025' - monthly reports are always within the same year\n return $from->format('M Y');\n\n case self::FREQUENCY_WEEKLY:\n // '4 - 8 Aug 2025', '27 Oct - 3 Nov 2025', or '28 Dec 2024 - 3 Jan 2025' if years differ\n $startDay = $from->format('j');\n $endDay = $to->format('j');\n $startMonth = $from->format('M');\n $endMonth = $to->format('M');\n\n if ($differentYears) {\n return \"{$startDay} {$startMonth} {$fromYear} - {$endDay} {$endMonth} {$toYear}\";\n }\n\n if ($startMonth !== $endMonth) {\n return \"{$startDay} {$startMonth} - {$endDay} {$endMonth} {$toYear}\";\n }\n\n return \"{$startDay} - {$endDay} {$endMonth} {$toYear}\";\n\n case self::FREQUENCY_ONE_OFF:\n // '2 May-31 May 2025' or '15 Dec 2024-15 Jan 2025' if years differ\n $startDay = $from->format('j');\n $startMonth = $from->format('M');\n $endDay = $to->format('j');\n $endMonth = $to->format('M');\n\n // If same month and year, use a format like '2-31 May 2025'\n if ($startMonth === $endMonth && ! $differentYears) {\n return \"{$startDay} - {$endDay} {$startMonth} {$toYear}\";\n }\n\n // If different years, include both years\n if ($differentYears) {\n return \"{$startDay} {$startMonth} {$fromYear} - {$endDay} {$endMonth} {$toYear}\";\n }\n\n // Same year but different months\n return \"{$startDay} {$startMonth} - {$endDay} {$endMonth} {$toYear}\";\n\n default:\n // Default format for unknown frequencies\n return $from->format('j M Y') . ' - ' . $to->format('j M Y');\n }\n }\n\n public function sanitizeFileName(string $fileName): string\n {\n return str_replace(['/', '\\\\'], '-', $fileName);\n }\n\n private function getPayload(AutomatedReportsService $automatedReportsService)\n {\n $reportResult = AutomatedReportResult::find(269);\n $automatedReport = $reportResult->getReport();\n $activityIds = [1,2,3];\n $payload = $automatedReportsService->getAskJiminnyGenerateReportPayload(\n automatedReport: $automatedReport,\n reportResult: $reportResult,\n activityIds: $activityIds,\n );\n\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$payload ' . PHP_EOL . print_r($payload, true));\n }\n\n private function rateLimit()\n {\n $team = Team::find(2);\n $config = $team->getCrmConfiguration();\n\n $crmResolver = app(CrmOwnerResolver::class, [\n 'team' => $team,\n 'integrationAdmin' => $team->getOwner(),\n 'providerSlug' => $config->getProviderName(),\n ]);\n\n $crmService = $crmResolver->prepareCrmService();\n\n for ($i = 0 ; $i < 3; $i++) {\n// if ($i % 25 === 0) {\n// $this->info(\"Syncing opportunity {$i}\");\n $this->info(\"Matching contact {$i}\");\n// }\n// $crmService->syncOpportunity('374720564');\n $crmService->matchByName('Robot');\n }\n }\n\n private function simulateMatchActivityStorm(int $teamId = 2, int $count = 100): void\n {\n $team = Team::find($teamId);\n $config = $team->getCrmConfiguration();\n\n $activities = Activity::query()\n ->where('crm_configuration_id', $config->getId())\n ->orderByDesc('id')\n ->limit($count)\n ->get();\n\n $this->info(\"Dispatching {$activities->count()} MatchActivityCrmData jobs (portal={$config->getId()})\");\n\n foreach ($activities as $activity) {\n MatchActivityCrmData::dispatch($activity->getId(), $config, true);\n }\n\n $this->info('Done. Watch logs and run jiminny:debug observeRateLimit to inspect cache state.');\n }\n\n private function simulateVerifyTaskStorm(int $teamId = 2, int $count = 100): void\n {\n $activities = Activity::query()\n ->where('team_id', $teamId)\n ->whereNotNull('crm_provider_id')\n ->orderByDesc('id')\n ->limit($count)\n ->get();\n\n $this->info(\"Dispatching {$activities->count()} VerifyActivityCrmTaskJob jobs\");\n\n foreach ($activities as $activity) {\n VerifyActivityCrmTaskJob::dispatch($activity->getId());\n }\n\n $this->info('Done.');\n }\n\n private function observeRateLimitCache(int $teamId = 2): void\n {\n $team = Team::find($teamId);\n $config = $team->getCrmConfiguration();\n $key = sprintf('hubspot:ratelimit:portal:%d', $config->getId());\n\n $value = Redis::get($key);\n $ttl = Redis::ttl($key);\n\n $this->info(\"Redis key: {$key}\");\n $this->info('Value: ' . ($value ?? '(empty)'));\n $this->info(\"TTL: {$ttl}s\");\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Show Replace Field","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Search History","depth":3,"on_screen":true,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"429","depth":4,"on_screen":true,"value":"429","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"New Line","depth":3,"on_screen":true,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Match Case","depth":3,"on_screen":true,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Words","depth":3,"on_screen":true,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Regex","depth":3,"on_screen":true,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Replace History","depth":3,"bounds":{"left":0.0,"top":0.0,"width":0.015277778,"height":0.024444444},"on_screen":false,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"Replace","depth":4,"on_screen":false,"role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"New Line","depth":3,"bounds":{"left":0.0,"top":0.0,"width":0.015277778,"height":0.024444444},"on_screen":false,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Preserve case","depth":3,"bounds":{"left":0.0,"top":0.0,"width":0.015277778,"height":0.024444444},"on_screen":false,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"0 results","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXButton","text":"Previous Occurrence","depth":4,"on_screen":true,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Occurrence","depth":4,"on_screen":true,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Filter Search Results","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Open in Window, Multiple Cursors","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXLink","text":"Click to highlight","depth":4,"on_screen":false,"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
7502182920420093367
|
3603243692738029995
|
click
|
accessibility
|
NULL
|
Project: faVsco.js, menu
JY-20725-handle-HS-search Project: faVsco.js, menu
JY-20725-handle-HS-search-rate-limit, menu
Start Listening for PHP Debug Connections
HandleHubspotRateLimitTest
Run 'HandleHubspotRateLimitTest'
Debug 'HandleHubspotRateLimitTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
5
133
11
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Console\Commands;
use Carbon\Carbon;
use Carbon\CarbonImmutable;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Redis;
use InvalidArgumentException;
use Jiminny\Jobs\AutomatedReports\RequestGenerateAskJiminnyReportJob;
use Jiminny\Jobs\AutomatedReports\SendReportMailJob;
use Jiminny\Jobs\Crm\Delete\VerifyActivityCrmTaskJob;
use Jiminny\Jobs\Crm\MatchActivityCrmData;
use Jiminny\Jobs\JobDispatcherInterface;
use Jiminny\Models\Activity;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\AutomatedReportResult;
use Jiminny\Models\Team;
use Jiminny\Models\User;
use Jiminny\Repositories\AutomatedReportsRepository;
use Jiminny\Services\Activity\CrmOwnerResolver;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
/**
* Class JiminnyDebugCommand
*
* @package Jiminny\Console\Commands
*/
class JiminnyDebugCommand extends Command
{
public const string FREQUENCY_DAILY = 'daily';
public const string FREQUENCY_WEEKLY = 'weekly';
public const string FREQUENCY_MONTHLY = 'monthly';
public const string FREQUENCY_QUARTERLY = 'quarterly';
public const string FREQUENCY_ONE_OFF = 'one_off';
protected $signature = 'jiminny:debug';
public function handle(
JobDispatcherInterface $jobDispatcher,
AutomatedReportsService $automatedReportsService,
AutomatedReportsRepository $automatedReportsRepository,
UserPilotClient $userPilotClient
): void {
// Choose ONE of the following to run, then comment out the others.
// 1) Dispatch a storm of MatchActivityCrmData jobs against team 2
$this->simulateMatchActivityStorm(teamId: 2, count: 100);
// 2) Dispatch a storm of VerifyActivityCrmTaskJob jobs (simulates DeleteCrmEntityTrait fan-out)
// $this->simulateVerifyTaskStorm(teamId: 2, count: 100);
// 3) Inspect Redis circuit-breaker state for the team's HubSpot portal
// $this->observeRateLimitCache(teamId: 2);
// 4) Make 3 synchronous matchByName calls (foreground, hits API directly)
// $this->rateLimit();
exit(1);
$report = AutomatedReport::find(71);
$last = AutomatedReportResult::query()
->where('report_id', $report->getId())
->whereIn('status', [AutomatedReportResult::STATUS_DEFAULT, AutomatedReportResult::STATUS_FAILED])
// ->where('reason', '!=', AutomatedReportResult::REASON_NOT_ENOUGH_ACTIVITIES)
->whereDate('created_at', CarbonImmutable::now()->toDateString())
->latest()
->first();
$this->info("Last: {$last->getId()}");
exit(1);
$user = User::find(143);
// $count = $automatedReportsRepository->countUserReports($user);
// $this->info("Count: {$count}");
// $count = $automatedReportsRepository->countAllUserReports($user);
// $this->info("All count: {$count}");
$payload = [
'report_type' => 'ask_jiminny',
'frequency' => 'weekly',
];
$userPilotClient->track($user, 'ask-jiminny-report-generated', $payload);
exit(1);
$now = Carbon::now()->subDay(1);
$this->info("Now: {$now->toDateTimeString()}");
$weekStart = Carbon::getWeekStartsAt();
$this->info("Now: {$weekStart}");
// $from = $now->copy()->previousWeekday()->startOfDay();
// $to = $now->copy()->previousWeekday()->endOfDay();
// $fromOld = $now->copy()->subWeeks(1)->startOfDay();
// $toOld = $now->copy()->subDay()->endOfDay();
// $fromNew = $now->copy()->subWeek()->startOfWeek();
// $toNew = $now->copy()->subWeek()->endOfWeek();
// $fromOld = $now->copy()->subMonths(1)->startOfDay();
// $toOld = $now->copy()->subDay()->endOfDay();
// $fromNew = $now->copy()->subMonthNoOverflow()->startOfMonth();
// $toNew = $now->copy()->subMonthNoOverflow()->endOfMonth();
$fromOld = $now->copy()->subMonths(3)->startOfDay();
$toOld = $now->copy()->subDay()->endOfDay();
$fromNew = $now->copy()->subQuarterNoOverflow()->startOfQuarter();
$toNew = $now->copy()->subQuarterNoOverflow()->endOfQuarter();
$this->info("From old: {$fromOld->toDateTimeString()}");
$this->info("To old: {$toOld->toDateTimeString()}");
$this->info("From new: {$fromNew->toDateTimeString()}");
$this->info("To new: {$toNew->toDateTimeString()}");
exit(1);
$report = AutomatedReport::find(71);
$job = new RequestGenerateAskJiminnyReportJob($report->getUuid());
$jobDispatcher->dispatch($job);
exit(1);
// $this->formatDate($jobDispatcher);
// $this->sendMail($jobDispatcher, $automatedReportsService);
// $this->crmService();
$this->getPayload($automatedReportsService);
exit(1);
}
private function crmService()
{
$activity = Activity::find(418141);
$team = Team::find(19);
$config = $team->getCrmConfiguration();
$crmResolver = app(CrmOwnerResolver::class, [
'team' => $team,
'integrationAdmin' => $team->getOwner(),
'providerSlug' => $config->getProviderName(),
]);
$crmService = $crmResolver->prepareCrmService();
$crmService->createTranscriptNotes($activity);
}
private function sendMail(JobDispatcherInterface $jobDispatcher, AutomatedReportsService $automatedReportsService)
{
$reportUuid = '';
// $report = $automatedReportsService->getReportResult($reportUuid);
$report = AutomatedReportResult::find(275);
$validRecipients = $automatedReportsService->getValidRecipientUsers(
$report->getReport(),
includeJiminny: true,
);
$recipient = $validRecipients[0];
$fileName = $automatedReportsService->getReportFileName($report);
$typeName = $report->getReport()->getCustomName()
?? $automatedReportsService->getReportTypeName($report);
$teamsName = $automatedReportsService->getReportTeamsName($report);
$periodName = $automatedReportsService->getReportPeriodName($report);
$s3Path = $automatedReportsService->getMediaPath($report);
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$fileName ' . PHP_EOL . print_r($fileName, true));
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$typeName ' . PHP_EOL . print_r($typeName, true));
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$teamsName ' . PHP_EOL . print_r($teamsName, true));
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$periodName ' . PHP_EOL . print_r($periodName, true));
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$s3Path ' . PHP_EOL . print_r($s3Path, true));
$jobDispatcher->dispatch(
new SendReportMailJob(
reportUuid: $report->getUuid(),
s3Path: $s3Path,
recipientEmail: $recipient['email'],
recipientName: $recipient['name'] ?? null,
fileName: $fileName,
typeName: $typeName,
teamsName: $teamsName,
periodName: $periodName,
isAskJiminny: true,
)
);
exit(1);
}
private function formatDate(JobDispatcherInterface $jobDispatcher): void
{
$customName = 'Custom report name';
// $frequency = self::FREQUENCY_DAILY;
// $frequency = self::FREQUENCY_WEEKLY;
$frequency = self::FREQUENCY_MONTHLY;
// $frequency = self::FREQUENCY_QUARTERLY;
// $frequency = self::FREQUENCY_ONE_OFF;
$period = $this->calculateFromAndToDatePeriod($frequency);
$from = $period['fromDate'];
$to = $period['toDate'];
$periodName = $this->formatReportPeriodName($frequency, $from, $to);
$filenameSuffix = null;
if ($customName) {
if ($filenameSuffix) {
$customName .= " {$filenameSuffix}";
}
$result = $this->sanitizeFileName("{$customName} - {$periodName}");
}
$this->info($result);
}
public function calculateFromAndToDatePeriod(
string $frequency,
?Carbon $fromDate = null,
?Carbon $toDate = null
): array {
if ($frequency === self::FREQUENCY_ONE_OFF) {
return [
'fromDate' => $fromDate,
'toDate' => $toDate,
];
}
$now = Carbon::now();
return match ($frequency) {
self::FREQUENCY_DAILY => [
'fromDate' => $now->copy()->subDay()->startOfDay(),
'toDate' => $now->copy()->subDay()->endOfDay(),
],
self::FREQUENCY_WEEKLY => [
'fromDate' => $now->copy()->subWeeks(1)->startOfDay(),
'toDate' => $now->copy()->subDay()->endOfDay(),
],
self::FREQUENCY_MONTHLY => [
'fromDate' => $now->copy()->subMonths(1)->startOfDay(),
'toDate' => $now->copy()->subDay()->endOfDay(),
],
self::FREQUENCY_QUARTERLY => [
'fromDate' => $now->copy()->subMonths(3)->startOfDay(),
'toDate' => $now->copy()->subDay()->endOfDay(),
],
default => throw new InvalidArgumentException("Unsupported frequency: {$frequency}"),
};
}
private function formatReportPeriodName(string $frequency, Carbon $from, Carbon $to): string
{
$fromYear = $from->format('Y');
$toYear = $to->format('Y');
$differentYears = $fromYear !== $toYear;
switch ($frequency) {
case self::FREQUENCY_DAILY:
return $from->format('j M Y');
case self::FREQUENCY_QUARTERLY:
// 'Jan-Mar 2025' or 'Nov 2024-Jan 2025' if years differ
$startMonth = $from->format('M');
$endMonth = $to->copy()->subMonth();
$endMonthName = $endMonth->format('M');
$endMonthYear = $endMonth->format('Y');
if ($differentYears) {
return "{$startMonth} {$fromYear} - {$endMonthName} {$endMonthYear}";
}
return "{$startMonth} - {$endMonthName} {$toYear}";
case self::FREQUENCY_MONTHLY:
// 'May 2025' - monthly reports are always within the same year
return $from->format('M Y');
case self::FREQUENCY_WEEKLY:
// '4 - 8 Aug 2025', '27 Oct - 3 Nov 2025', or '28 Dec 2024 - 3 Jan 2025' if years differ
$startDay = $from->format('j');
$endDay = $to->format('j');
$startMonth = $from->format('M');
$endMonth = $to->format('M');
if ($differentYears) {
return "{$startDay} {$startMonth} {$fromYear} - {$endDay} {$endMonth} {$toYear}";
}
if ($startMonth !== $endMonth) {
return "{$startDay} {$startMonth} - {$endDay} {$endMonth} {$toYear}";
}
return "{$startDay} - {$endDay} {$endMonth} {$toYear}";
case self::FREQUENCY_ONE_OFF:
// '2 May-31 May 2025' or '15 Dec 2024-15 Jan 2025' if years differ
$startDay = $from->format('j');
$startMonth = $from->format('M');
$endDay = $to->format('j');
$endMonth = $to->format('M');
// If same month and year, use a format like '2-31 May 2025'
if ($startMonth === $endMonth && ! $differentYears) {
return "{$startDay} - {$endDay} {$startMonth} {$toYear}";
}
// If different years, include both years
if ($differentYears) {
return "{$startDay} {$startMonth} {$fromYear} - {$endDay} {$endMonth} {$toYear}";
}
// Same year but different months
return "{$startDay} {$startMonth} - {$endDay} {$endMonth} {$toYear}";
default:
// Default format for unknown frequencies
return $from->format('j M Y') . ' - ' . $to->format('j M Y');
}
}
public function sanitizeFileName(string $fileName): string
{
return str_replace(['/', '\\'], '-', $fileName);
}
private function getPayload(AutomatedReportsService $automatedReportsService)
{
$reportResult = AutomatedReportResult::find(269);
$automatedReport = $reportResult->getReport();
$activityIds = [1,2,3];
$payload = $automatedReportsService->getAskJiminnyGenerateReportPayload(
automatedReport: $automatedReport,
reportResult: $reportResult,
activityIds: $activityIds,
);
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$payload ' . PHP_EOL . print_r($payload, true));
}
private function rateLimit()
{
$team = Team::find(2);
$config = $team->getCrmConfiguration();
$crmResolver = app(CrmOwnerResolver::class, [
'team' => $team,
'integrationAdmin' => $team->getOwner(),
'providerSlug' => $config->getProviderName(),
]);
$crmService = $crmResolver->prepareCrmService();
for ($i = 0 ; $i < 3; $i++) {
// if ($i % 25 === 0) {
// $this->info("Syncing opportunity {$i}");
$this->info("Matching contact {$i}");
// }
// $crmService->syncOpportunity('374720564');
$crmService->matchByName('Robot');
}
}
private function simulateMatchActivityStorm(int $teamId = 2, int $count = 100): void
{
$team = Team::find($teamId);
$config = $team->getCrmConfiguration();
$activities = Activity::query()
->where('crm_configuration_id', $config->getId())
->orderByDesc('id')
->limit($count)
->get();
$this->info("Dispatching {$activities->count()} MatchActivityCrmData jobs (portal={$config->getId()})");
foreach ($activities as $activity) {
MatchActivityCrmData::dispatch($activity->getId(), $config, true);
}
$this->info('Done. Watch logs and run jiminny:debug observeRateLimit to inspect cache state.');
}
private function simulateVerifyTaskStorm(int $teamId = 2, int $count = 100): void
{
$activities = Activity::query()
->where('team_id', $teamId)
->whereNotNull('crm_provider_id')
->orderByDesc('id')
->limit($count)
->get();
$this->info("Dispatching {$activities->count()} VerifyActivityCrmTaskJob jobs");
foreach ($activities as $activity) {
VerifyActivityCrmTaskJob::dispatch($activity->getId());
}
$this->info('Done.');
}
private function observeRateLimitCache(int $teamId = 2): void
{
$team = Team::find($teamId);
$config = $team->getCrmConfiguration();
$key = sprintf('hubspot:ratelimit:portal:%d', $config->getId());
$value = Redis::get($key);
$ttl = Redis::ttl($key);
$this->info("Redis key: {$key}");
$this->info('Value: ' . ($value ?? '(empty)'));
$this->info("TTL: {$ttl}s");
}
}
Show Replace Field
Search History
429
New Line
Match Case
Words
Regex
Replace History
Replace
New Line
Preserve case
0 results
Previous Occurrence
Next Occurrence
Filter Search Results
Open in Window, Multiple Cursors
Click to highlight...
|
18239
|
NULL
|
NULL
|
NULL
|
|
18243
|
791
|
27
|
2026-05-11T11:06:46.986329+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-11/1778 /Users/lukas/.screenpipe/data/data/2026-05-11/1778497606986_m2.jpg...
|
PhpStorm
|
faVsco.js – laravel.log
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
JY-20725-handle-HS-search Project: faVsco.js, menu
JY-20725-handle-HS-search-rate-limit, menu
Start Listening for PHP Debug Connections
HandleHubspotRateLimitTest
Run 'HandleHubspotRateLimitTest'
Debug 'HandleHubspotRateLimitTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
5
133
11
Previous Highlighted Error
Next Highlighted Error...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"bounds":{"left":0.025930852,"top":0.019952115,"width":0.03856383,"height":0.025538707},"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JY-20725-handle-HS-search-rate-limit, menu","depth":5,"bounds":{"left":0.064494684,"top":0.019952115,"width":0.09541223,"height":0.025538707},"on_screen":true,"help_text":"Git Branch: JY-20725-handle-HS-search-rate-limit","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"bounds":{"left":0.82413566,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"HandleHubspotRateLimitTest","depth":6,"bounds":{"left":0.8394282,"top":0.019952115,"width":0.076130316,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'HandleHubspotRateLimitTest'","depth":6,"bounds":{"left":0.9155585,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'HandleHubspotRateLimitTest'","depth":6,"bounds":{"left":0.9268617,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"bounds":{"left":0.9381649,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"bounds":{"left":0.96609044,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"bounds":{"left":0.9773936,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"bounds":{"left":0.9886968,"top":0.019952115,"width":0.011303186,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"5","depth":4,"bounds":{"left":0.5043218,"top":0.17478053,"width":0.007978723,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"133","depth":4,"bounds":{"left":0.5142952,"top":0.17478053,"width":0.011968086,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"11","depth":4,"bounds":{"left":0.52825797,"top":0.17478053,"width":0.008976064,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.53889626,"top":0.17318435,"width":0.00731383,"height":0.018355945},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.5462101,"top":0.17318435,"width":0.006981383,"height":0.018355945},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
417956753040334189
|
-8740335085574583358
|
click
|
hybrid
|
NULL
|
Project: faVsco.js, menu
JY-20725-handle-HS-search Project: faVsco.js, menu
JY-20725-handle-HS-search-rate-limit, menu
Start Listening for PHP Debug Connections
HandleHubspotRateLimitTest
Run 'HandleHubspotRateLimitTest'
Debug 'HandleHubspotRateLimitTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
5
133
11
Previous Highlighted Error
Next Highlighted Error
PhostormVIewINavicareCodeLaravelKeractorTOOISWindowFV faVsco.js%9 JY-20725-handle-HS-search-rate-limit-Project© TrackAutomatedReportGeneratedEvent.php(C) TranscodeParameterRescl© UserService.php(c) Uuid.pnp> O TraitsDUseCases> DUser> D Utils> D Validation> OvOphp nelpers.ongInitialFrontendState.php© Jiminny.phpc) Plan.oho© Serializer.phpC) TeamScimDetails.ohpbootstrap> Do build> contia> O contrib→ database>O docsM front-end> 0 lang> node_modules library root> O phpstan> M nublic› Dresourcesv Mroutesphp api.phpphp api_v2.phppnp console.onopnp customer_api.onppnp embedded.ongphp nealtn.onppnp scim.onophp uprotectedweb.phpphp web.phpphp webhook.php>O scripts~D storage•aoo> D debugbar… M frameworkv Mloas.aitianore• audio wav= custom.loal* SyncCrmEntities Trait.php©CachedCrmServiceDecorator.php© RateLimitExce© PaginationConfia.phdclass JiminnyDebuqcommand extends Command361private function natelamitfor ($i = 0 ; $i < 3; $i++) {if ($i % 25 === 0) {363364scnis"pinto syneine opporcunity 11r$this->info( string: "Matching contact ($1}");367368ScrmService->syncOpportunity('374720564');scrmservice->macchbyname "kobot379=hubsnot-iournal-noll.log= laravel log< nhnunit ymus tht isE oauth-nrivate kevTests passed: 13 (today 13:02)© HubSpot/Service.php* DeleteCrmEntity Trait.phpoveryaeuvitycrmlaskJoo.ongт I.A5 A133 X11 A Y1 usageprivate function simulateMatchActivityStorm(int Steamid = 2, int Scount = 100): voidSconfio = steam->oetcrmconflourat.ion0Sactivities = Activitv:quervo'crm_configuration_id', $config->getId())->orderBvDescdi->Limit($count)->get();$this->info( string: "Dispatching (Sactivities->count()} MatchActivityCrmData jobs (portal={$config->getId()})");foreach (Sactivities as $activity) {MatchActivityCrmData: :dispatch( …..arguments: $activity->getId(), $config, true);Sthis->info( string:) 'Done. Watch Logs and run jiminny:debug observeRateLimit to inspect cache state.');no usagesprivate function simulateVerifyTaskStorm(int SteamId = 2, int $count = 100): voidSactavitles = Activity::quervol>where co'team_1d', Steamid)s: 'crm provider id')'1d)->net05Sthis->info( string: "Dispatching (Sactivities->count} VerifyActivityCrmiaskJob jobs");foneach (Sactivities as Cactivitv) !Preparation tor kell.. In 34mMon 11 May 14:06:46E custom.logA SF [jiminny@localhost]A console (EU]A console [STAGING]307311320[326328330=331_333E336338=339=352[2026-05-11 11:06:17] Local. INFO: [EmailSchedule] FINISHED batch process f("host": "docker_Lamp_1", "processed":0} {"correlation_id":"dceccba8-9e9 =2026-05-11 11:06:17| LocoL.INF0: Jiminny Console Commands Command: :run[2026-05-11 11:06:17] Local.INF0: [MatchActivityCrmData] Starting CRM data[2026-05-11 11:06:17] Local.INF0: [ EsUpdateTarget ] Update single target {"targ[2026-05-11 11:06:17] Local. INFO: [ AsyncUpdateElasticSearch ] Entity added to R[2026-05-11 11:06:17] Local.INFO: [MatchActivityCrmData] Participants old state {"activity":612181,"[2026-05-11 11:06:17] local. INFO: [SocialAccountService] Fetching token -[2026-05-11 11:06:17] Local. INFO: [SocialAccountService] Token retrieved[2026-05-11 11:06:17] Local. INFO: [EncryptedTokenManager] Generating access token.[2026-05-11 11:06:17] Local. INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner[2026-05-11 11:06:17] Local. INFO: [Prospect match] Cache / local search hit {"identifier_type":"email", "identifier":"robins[2026-05-11 11:06:17] Local. INFO: [CrmActivityService][2026-05-11 11:06:17] Local.INFO: [CrmActivityService] CRM matching[2026-05-11 11:06:17] Local. INFO: [ EsUpdateTarget ] Update single target[2026-05-11 11:06:17] Local. INFO: [ AsyncUpdateElasticSearch ] Entity[2026-05-11 11:06:17] Local. INFO: [MatchActivityCrmDatal Successfully2026-05-11 11:06:17 LocaL.INFU: MatchActivicyurmuata Starcino ukm data[2026-05-11 11:06:17] Local. INFO: [ EsUpdateTarget ] Update single target {"ta[2026-05-11 11:06:17] local.INF0: [ AsyncUpdateElasticSearch ] Entity[2026-05-11 11:06:17] Local. INFO: [MatchActivityCrmDatal Participants12026-05-11 11:06:18 LocaL.INF0:SocialAccountServicell[2026-05-11 11:06:18] Local. INFO: [SocialAccountServicel Token retriev2026-05-11 11:06:18| LocaL.INFD: EncrvotedTokenlanager Generatina access token[2026-05-11 11:06:18] Local. INFO: [Crm012026-05-11 11:06:18| Locol.INF0: Prospect matchi[2026-05-11 11:06:18] Local. INFO: [CrmAEsUpdateTarget ] Update single12026-05-11 11:06:181 loco1TNS0: | AsvnclndateFlasticSearch1 Entity added to Redis12026-05-11 11:06:181 loco1.TNS0: [MatchActivitvCrmDatal Startino CRM datal[2026-05-11 11:06:18] Local. INFO: [ EsUpdateTarget ] Update single target {"tar12026-05-11 11•06•191 1ocol TAS0• 1 AcvnclindateFlacticCeanch Entity added to Redic 1ict[2026-05-11 11:06:18] Local. INFO: [MatchActivityCrmData] Participants old state ("activity":611455, "partici[2026-05-11 11:06:18] Local.INFO: [SocialAccountService] Fetching token {"socialAccountId":1499[2026-05-11 11:06:18] Local. INFO: [SocialAccountService] Token retrieved {"socialAc[2026-05-11 11:06:18] Local. INFO: [EncryptedTokenManager] Generating access token.[2026-05-11 11:06:18] Local. INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {"crm_pr[2026-05-11 11:06:18] Local. INFO: [Prospect match] Cache miss,[2026-05-11 11:06:18] Local.INFO: [Hubspot] Failed to fetch contact {"email":"[EMAIL][2026-05-11 11:06:18] Local. INFO: [Prospect match] API returned empty result,caching the miss withe[2026-05-11 11:06:18] Local. INFO: [Hubs[2026-05-11 11:06:18] Local. INFO: [Pros12026-05-11 11:06:181 LocaL.INF0: Pro:[2026-05-11 11:06:18] Local. INFO: [Pros12026-05-11 11:06:191 LocaL.INF0: HUb.[2026-05-11 11:06:19] Local. INFO: [HubS12026-05-11 11:06:191 LocoL TNF0: Hub*299c67e4-7026-46ee-b10г00c/704-7024-4600-h1Ad-dhe/8fh5Sa7c" ":...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
18244
|
791
|
28
|
2026-05-11T11:06:48.636748+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-11/1778 /Users/lukas/.screenpipe/data/data/2026-05-11/1778497608636_m2.jpg...
|
PhpStorm
|
faVsco.js – custom.log
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
JY-20725-handle-HS-search Project: faVsco.js, menu
JY-20725-handle-HS-search-rate-limit, menu...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"bounds":{"left":0.025930852,"top":0.019952115,"width":0.03856383,"height":0.025538707},"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JY-20725-handle-HS-search-rate-limit, menu","depth":5,"bounds":{"left":0.064494684,"top":0.019952115,"width":0.09541223,"height":0.025538707},"on_screen":true,"help_text":"Git Branch: JY-20725-handle-HS-search-rate-limit","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
-5641617897080429754
|
-8160223333407913180
|
visual_change
|
hybrid
|
NULL
|
Project: faVsco.js, menu
JY-20725-handle-HS-search Project: faVsco.js, menu
JY-20725-handle-HS-search-rate-limit, menu
PhostormVIewINavicareCodeLaravelKeractorlooIsWindowFV faVsco.js?9 JY-20725-handle-HS-search-rate-limitProiectC) TrackAutomated ReportGeneratedzventonp(C) TranscodeParameterRescl© UserService.php(c) Uuid.pnp> D TraitsD UseCases> D User> D Utils› D Validation> OvOphp nelpers.ong© InitialFrontendState.php© Jiminny.phpc) Plan.oho© Serializer.phpC) TeamScimDetails.ohpbootstrap>© build.> contia>D contrib→ database>O docsM front-end>D lang> node_modules library rootM ohostan> M nublic>O resourcesv Mroutesphp api.phpphp api_v2.phppnp console.onopnp customer_api.onppnp embedded.ongphp nealtn.onppnp scim.onophp uprotectedweb.phpphp web.phpphp webhook.php>O scriptsv O storage•aoo> M debuabar.… M frameworkv Mloas.aitianore• audio wav= custom.loalT SyncCrmEntitiesTrait.phpC) CachedCrmServiceDecorator.ongDeleteCrmEntityTrait.phpoveryaeuvitycrmlaskJoo.ong© RateLimitExce© PaginationConfia.phdclass JiminnyDebuqcommand extends Command361363364367368379=hubsnot-iournal-noll.log= laravel log< nhnunit ymus tht is= oauth-nrivate kevTacts naccod. 12 (todav 12:02private function natelamitfor (Si = 0 ; $i < 3; $i++) {if ($i % 25 === 0) {scnis"pinto syneine opporcunity 11rSthis->info( string: "Matching contact {Si}"):scrmservice->syncupporcun1cy574/20504scrmservice->macchbyname "kobot"1 usageprivate function simulateMatchActivityStorm(int Steamid = 2, int Scount = 100): voidSconfio = steam->oetcrmconflourat.ion0Sactivities = Activitv:quervoerm confiauration id' Sconfio->aetidon->orderBvDescdi->Uimit(Scount)->getO:Sthis->info( string: "Dispatching {Sactivities->count} MatchActivityCrmData jobs (portal={$config->getidO})"):fonpach (Sactivities as Cactivitv) &MatchActivityCrmData::dispatch( ...arguments: Sactivity->getId, $config, true):Sthis->info( string: 'Done. Watch logs and run jiminny:debug observeRateLimit to inspect cache state.'):no usagesprivate function simulateVerifyTaskStorm(int SteamId = 2, int $count = 100): voidSactavitles = Activity::quervol>where co'team_1d', Steamid)s: 'crm provider id')'1d)->net05Sthis->info( string: "Dispatching (Sactivities->count} VerifyActivityCrmTaskJob jobs");foneach (Sactivities as Sactivitv) $(C) HubSpot/Service.php© ProviderkateLimiter.pngM A5 A133 X11 ^ v313318322- 328329330332=333=337[341345346Mon 11 May 14:06:48A console [EUiconsole [STAGINGI[2026-05-11 11:06:17] local.INF0: [MatchActivityCrmDatal Starting CRM data matching {"activity":612181,"remote search":true, "set confiquration"2026-05-11 11:06:17 Local.INFU:EsUpdatelarget Update sinqle taroet ""target'':"activitles", "purpose": "searchable-observer-update","entityld":612181r "co.2026-05-11 11:06:17 Local.INFU:12026-05-11 11:06117 LocoLINFI: MatchActivirvermbatal Particioants old state &'activit432a-b95112026-05-11 11:06:17 LocoL,INF0:SocialAccountServicell2026-05-11 11:06:171 LocoL,INF0:CrmOwnerResolveri Intearation owner12026-05-11 11:06:171 LocaL,INF0: Prosoect matchl Cac2026-05-11 11:06:171 LocaLINF0:CrmActivitvServicel[2026-05-11 11:06:17) local.INFO: [CrmActivityService.12026-05-11 11•06-171 Toco1NS0 ScundateTaraet " Undate sinale target[2026-05-11 11:06:17] local.INF0: [ AsyncUpdateElasticSearch ) Entity added to Redis list {"entityType":"activities"."entityId":612181,"colj[2026-05-11 11:06:17] local.INF0: [MatchActivityCrmData] Successfully matched CRM data[2026-05-11 11:06:17] local.INF0: [MatchActivityCrmData] Starting CRM data[2026-05-11 11:06:17] local.INF0: [ EsUpdateTarget ] Update single target {"tar[2026-05-11 11:06:17] local.INF0: [ AsyncUpdateElasticSearch ] Entity added to Redis list {"entityTy[2026-05-11 11:06:17] local.INF0: [MatchActivityCrmData] Participants old state {"a[2026-05-11 11:06:18] local.INF0: [SocialAccountService] Fetching token {"socialAco[2026-05-11 11:06:18] local.INF0: [SocialAccountServicel Token retrieved {"socialAc2026-05-11 11:06:18 Local.INFU: EncryptedlokenManager beneratina[2026-05-11 11:06:18] local.INF0: [Crm0wnerResolver] Integration2026-05-11 11:06:18 LocaL.INFU: Pro:[2026-05-11 11:06:18] local.INF0:12026-05-11 11:06:18 Local.LNFU: CrmActiv1tyservicel12026-05-11 11:06118 LOCOL.INFU: ASVN[2026-05-11 11:06:181 local,INF0: [MatchActivitvCrmDatal Si2026-05-11 11:06:18| Locol.INF0: MatchActivitvcrmbatall2026-05-11 11:06:18 LocoL. INFO*2026-05-11111:06:18 LocoL.INF0: AsvncUodateElasticSearch12026-05-11 11:06:18| LocoL.INFD: MatchActivitvermbatal Particinants old12026-05-1112026-05-11 11:06:181 localTNS0• [SocialAccountServicel12026-05-11 11•06-181 locolTNE0• EncrvntedTokenManager Genenatina access token.2026-05-11 11•06:181 localTNE0• 1Crmi12026-05-11 11•06•181 locolTNE0• Prosnect matchll Cache miss. callina the APT "identifien tvne"."email[2026-05-11 11:06:18] local.INF0: [Hubspo12024-05-11 11•04•191 Tocal TASП• 1Pno.[2026-05-11 11:06:18] local.INF0: [Hubspot] Pagination completed {"team_id":2,"endpoint":"https://api.hu[2026-05-11 11:06:18] local.INF0: [Prospect match] Resolved company domain from email {"email":"[EMAIL]"."domain":"jiminny.com[2026-05-11 11:06:18] local.INF0: [Pro:[2026-05-11 11:06:18] local.INF0: [Pro:[2026-05-11 11:06:19] local.INF0: [Hubs1г2004-05-11 11-04.101 Лосо1 ТМS0• ГHuhSno+1[2026-05-11 11:06:19] local.INF0: [Hub:P A enasoc...
|
18243
|
NULL
|
NULL
|
NULL
|
|
18245
|
790
|
28
|
2026-05-11T11:06:51.039610+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-11/1778 /Users/lukas/.screenpipe/data/data/2026-05-11/1778497611039_m1.jpg...
|
PhpStorm
|
faVsco.js – laravel.log
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
JY-20725-handle-HS-search Project: faVsco.js, menu
JY-20725-handle-HS-search-rate-limit, menu
Start Listening for PHP Debug Connections
HandleHubspotRateLimitTest
Run 'HandleHubspotRateLimitTest'
Debug 'HandleHubspotRateLimitTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
5
133
11
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Console\Commands;
use Carbon\Carbon;
use Carbon\CarbonImmutable;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Redis;
use InvalidArgumentException;
use Jiminny\Jobs\AutomatedReports\RequestGenerateAskJiminnyReportJob;
use Jiminny\Jobs\AutomatedReports\SendReportMailJob;
use Jiminny\Jobs\Crm\Delete\VerifyActivityCrmTaskJob;
use Jiminny\Jobs\Crm\MatchActivityCrmData;
use Jiminny\Jobs\JobDispatcherInterface;
use Jiminny\Models\Activity;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\AutomatedReportResult;
use Jiminny\Models\Team;
use Jiminny\Models\User;
use Jiminny\Repositories\AutomatedReportsRepository;
use Jiminny\Services\Activity\CrmOwnerResolver;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
/**
* Class JiminnyDebugCommand
*
* @package Jiminny\Console\Commands
*/
class JiminnyDebugCommand extends Command
{
public const string FREQUENCY_DAILY = 'daily';
public const string FREQUENCY_WEEKLY = 'weekly';
public const string FREQUENCY_MONTHLY = 'monthly';
public const string FREQUENCY_QUARTERLY = 'quarterly';
public const string FREQUENCY_ONE_OFF = 'one_off';
protected $signature = 'jiminny:debug';
public function handle(
JobDispatcherInterface $jobDispatcher,
AutomatedReportsService $automatedReportsService,
AutomatedReportsRepository $automatedReportsRepository,
UserPilotClient $userPilotClient
): void {
// Choose ONE of the following to run, then comment out the others.
// 1) Dispatch a storm of MatchActivityCrmData jobs against team 2
$this->simulateMatchActivityStorm(teamId: 2, count: 100);
// 2) Dispatch a storm of VerifyActivityCrmTaskJob jobs (simulates DeleteCrmEntityTrait fan-out)
// $this->simulateVerifyTaskStorm(teamId: 2, count: 100);
// 3) Inspect Redis circuit-breaker state for the team's HubSpot portal
// $this->observeRateLimitCache(teamId: 2);
// 4) Make 3 synchronous matchByName calls (foreground, hits API directly)
// $this->rateLimit();
exit(1);
$report = AutomatedReport::find(71);
$last = AutomatedReportResult::query()
->where('report_id', $report->getId())
->whereIn('status', [AutomatedReportResult::STATUS_DEFAULT, AutomatedReportResult::STATUS_FAILED])
// ->where('reason', '!=', AutomatedReportResult::REASON_NOT_ENOUGH_ACTIVITIES)
->whereDate('created_at', CarbonImmutable::now()->toDateString())
->latest()
->first();
$this->info("Last: {$last->getId()}");
exit(1);
$user = User::find(143);
// $count = $automatedReportsRepository->countUserReports($user);
// $this->info("Count: {$count}");
// $count = $automatedReportsRepository->countAllUserReports($user);
// $this->info("All count: {$count}");
$payload = [
'report_type' => 'ask_jiminny',
'frequency' => 'weekly',
];
$userPilotClient->track($user, 'ask-jiminny-report-generated', $payload);
exit(1);
$now = Carbon::now()->subDay(1);
$this->info("Now: {$now->toDateTimeString()}");
$weekStart = Carbon::getWeekStartsAt();
$this->info("Now: {$weekStart}");
// $from = $now->copy()->previousWeekday()->startOfDay();
// $to = $now->copy()->previousWeekday()->endOfDay();
// $fromOld = $now->copy()->subWeeks(1)->startOfDay();
// $toOld = $now->copy()->subDay()->endOfDay();
// $fromNew = $now->copy()->subWeek()->startOfWeek();
// $toNew = $now->copy()->subWeek()->endOfWeek();
// $fromOld = $now->copy()->subMonths(1)->startOfDay();
// $toOld = $now->copy()->subDay()->endOfDay();
// $fromNew = $now->copy()->subMonthNoOverflow()->startOfMonth();
// $toNew = $now->copy()->subMonthNoOverflow()->endOfMonth();
$fromOld = $now->copy()->subMonths(3)->startOfDay();
$toOld = $now->copy()->subDay()->endOfDay();
$fromNew = $now->copy()->subQuarterNoOverflow()->startOfQuarter();
$toNew = $now->copy()->subQuarterNoOverflow()->endOfQuarter();
$this->info("From old: {$fromOld->toDateTimeString()}");
$this->info("To old: {$toOld->toDateTimeString()}");
$this->info("From new: {$fromNew->toDateTimeString()}");
$this->info("To new: {$toNew->toDateTimeString()}");
exit(1);
$report = AutomatedReport::find(71);
$job = new RequestGenerateAskJiminnyReportJob($report->getUuid());
$jobDispatcher->dispatch($job);
exit(1);
// $this->formatDate($jobDispatcher);
// $this->sendMail($jobDispatcher, $automatedReportsService);
// $this->crmService();
$this->getPayload($automatedReportsService);
exit(1);
}
private function crmService()
{
$activity = Activity::find(418141);
$team = Team::find(19);
$config = $team->getCrmConfiguration();
$crmResolver = app(CrmOwnerResolver::class, [
'team' => $team,
'integrationAdmin' => $team->getOwner(),
'providerSlug' => $config->getProviderName(),
]);
$crmService = $crmResolver->prepareCrmService();
$crmService->createTranscriptNotes($activity);
}
private function sendMail(JobDispatcherInterface $jobDispatcher, AutomatedReportsService $automatedReportsService)
{
$reportUuid = '';
// $report = $automatedReportsService->getReportResult($reportUuid);
$report = AutomatedReportResult::find(275);
$validRecipients = $automatedReportsService->getValidRecipientUsers(
$report->getReport(),
includeJiminny: true,
);
$recipient = $validRecipients[0];
$fileName = $automatedReportsService->getReportFileName($report);
$typeName = $report->getReport()->getCustomName()
?? $automatedReportsService->getReportTypeName($report);
$teamsName = $automatedReportsService->getReportTeamsName($report);
$periodName = $automatedReportsService->getReportPeriodName($report);
$s3Path = $automatedReportsService->getMediaPath($report);
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$fileName ' . PHP_EOL . print_r($fileName, true));
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$typeName ' . PHP_EOL . print_r($typeName, true));
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$teamsName ' . PHP_EOL . print_r($teamsName, true));
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$periodName ' . PHP_EOL . print_r($periodName, true));
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$s3Path ' . PHP_EOL . print_r($s3Path, true));
$jobDispatcher->dispatch(
new SendReportMailJob(
reportUuid: $report->getUuid(),
s3Path: $s3Path,
recipientEmail: $recipient['email'],
recipientName: $recipient['name'] ?? null,
fileName: $fileName,
typeName: $typeName,
teamsName: $teamsName,
periodName: $periodName,
isAskJiminny: true,
)
);
exit(1);
}
private function formatDate(JobDispatcherInterface $jobDispatcher): void
{
$customName = 'Custom report name';
// $frequency = self::FREQUENCY_DAILY;
// $frequency = self::FREQUENCY_WEEKLY;
$frequency = self::FREQUENCY_MONTHLY;
// $frequency = self::FREQUENCY_QUARTERLY;
// $frequency = self::FREQUENCY_ONE_OFF;
$period = $this->calculateFromAndToDatePeriod($frequency);
$from = $period['fromDate'];
$to = $period['toDate'];
$periodName = $this->formatReportPeriodName($frequency, $from, $to);
$filenameSuffix = null;
if ($customName) {
if ($filenameSuffix) {
$customName .= " {$filenameSuffix}";
}
$result = $this->sanitizeFileName("{$customName} - {$periodName}");
}
$this->info($result);
}
public function calculateFromAndToDatePeriod(
string $frequency,
?Carbon $fromDate = null,
?Carbon $toDate = null
): array {
if ($frequency === self::FREQUENCY_ONE_OFF) {
return [
'fromDate' => $fromDate,
'toDate' => $toDate,
];
}
$now = Carbon::now();
return match ($frequency) {
self::FREQUENCY_DAILY => [
'fromDate' => $now->copy()->subDay()->startOfDay(),
'toDate' => $now->copy()->subDay()->endOfDay(),
],
self::FREQUENCY_WEEKLY => [
'fromDate' => $now->copy()->subWeeks(1)->startOfDay(),
'toDate' => $now->copy()->subDay()->endOfDay(),
],
self::FREQUENCY_MONTHLY => [
'fromDate' => $now->copy()->subMonths(1)->startOfDay(),
'toDate' => $now->copy()->subDay()->endOfDay(),
],
self::FREQUENCY_QUARTERLY => [
'fromDate' => $now->copy()->subMonths(3)->startOfDay(),
'toDate' => $now->copy()->subDay()->endOfDay(),
],
default => throw new InvalidArgumentException("Unsupported frequency: {$frequency}"),
};
}
private function formatReportPeriodName(string $frequency, Carbon $from, Carbon $to): string
{
$fromYear = $from->format('Y');
$toYear = $to->format('Y');
$differentYears = $fromYear !== $toYear;
switch ($frequency) {
case self::FREQUENCY_DAILY:
return $from->format('j M Y');
case self::FREQUENCY_QUARTERLY:
// 'Jan-Mar 2025' or 'Nov 2024-Jan 2025' if years differ
$startMonth = $from->format('M');
$endMonth = $to->copy()->subMonth();
$endMonthName = $endMonth->format('M');
$endMonthYear = $endMonth->format('Y');
if ($differentYears) {
return "{$startMonth} {$fromYear} - {$endMonthName} {$endMonthYear}";
}
return "{$startMonth} - {$endMonthName} {$toYear}";
case self::FREQUENCY_MONTHLY:
// 'May 2025' - monthly reports are always within the same year
return $from->format('M Y');
case self::FREQUENCY_WEEKLY:
// '4 - 8 Aug 2025', '27 Oct - 3 Nov 2025', or '28 Dec 2024 - 3 Jan 2025' if years differ
$startDay = $from->format('j');
$endDay = $to->format('j');
$startMonth = $from->format('M');
$endMonth = $to->format('M');
if ($differentYears) {
return "{$startDay} {$startMonth} {$fromYear} - {$endDay} {$endMonth} {$toYear}";
}
if ($startMonth !== $endMonth) {
return "{$startDay} {$startMonth} - {$endDay} {$endMonth} {$toYear}";
}
return "{$startDay} - {$endDay} {$endMonth} {$toYear}";
case self::FREQUENCY_ONE_OFF:
// '2 May-31 May 2025' or '15 Dec 2024-15 Jan 2025' if years differ
$startDay = $from->format('j');
$startMonth = $from->format('M');
$endDay = $to->format('j');
$endMonth = $to->format('M');
// If same month and year, use a format like '2-31 May 2025'
if ($startMonth === $endMonth && ! $differentYears) {
return "{$startDay} - {$endDay} {$startMonth} {$toYear}";
}
// If different years, include both years
if ($differentYears) {
return "{$startDay} {$startMonth} {$fromYear} - {$endDay} {$endMonth} {$toYear}";
}
// Same year but different months
return "{$startDay} {$startMonth} - {$endDay} {$endMonth} {$toYear}";
default:
// Default format for unknown frequencies
return $from->format('j M Y') . ' - ' . $to->format('j M Y');
}
}
public function sanitizeFileName(string $fileName): string
{
return str_replace(['/', '\\'], '-', $fileName);
}
private function getPayload(AutomatedReportsService $automatedReportsService)
{
$reportResult = AutomatedReportResult::find(269);
$automatedReport = $reportResult->getReport();
$activityIds = [1,2,3];
$payload = $automatedReportsService->getAskJiminnyGenerateReportPayload(
automatedReport: $automatedReport,
reportResult: $reportResult,
activityIds: $activityIds,
);
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$payload ' . PHP_EOL . print_r($payload, true));
}
private function rateLimit()
{
$team = Team::find(2);
$config = $team->getCrmConfiguration();
$crmResolver = app(CrmOwnerResolver::class, [
'team' => $team,
'integrationAdmin' => $team->getOwner(),
'providerSlug' => $config->getProviderName(),
]);
$crmService = $crmResolver->prepareCrmService();
for ($i = 0 ; $i < 3; $i++) {
// if ($i % 25 === 0) {
// $this->info("Syncing opportunity {$i}");
$this->info("Matching contact {$i}");
// }
// $crmService->syncOpportunity('374720564');
$crmService->matchByName('Robot');
}
}
private function simulateMatchActivityStorm(int $teamId = 2, int $count = 100): void
{
$team = Team::find($teamId);
$config = $team->getCrmConfiguration();
$activities = Activity::query()
->where('crm_configuration_id', $config->getId())
->orderByDesc('id')
->limit($count)
->get();
$this->info("Dispatching {$activities->count()} MatchActivityCrmData jobs (portal={$config->getId()})");
foreach ($activities as $activity) {
MatchActivityCrmData::dispatch($activity->getId(), $config, true);
}
$this->info('Done. Watch logs and run jiminny:debug observeRateLimit to inspect cache state.');
}
private function simulateVerifyTaskStorm(int $teamId = 2, int $count = 100): void
{
$activities = Activity::query()
->where('team_id', $teamId)
->whereNotNull('crm_provider_id')
->orderByDesc('id')
->limit($count)
->get();
$this->info("Dispatching {$activities->count()} VerifyActivityCrmTaskJob jobs");
foreach ($activities as $activity) {
VerifyActivityCrmTaskJob::dispatch($activity->getId());
}
$this->info('Done.');
}
private function observeRateLimitCache(int $teamId = 2): void
{
$team = Team::find($teamId);
$config = $team->getCrmConfiguration();
$key = sprintf('hubspot:ratelimit:portal:%d', $config->getId());
$value = Redis::get($key);
$ttl = Redis::ttl($key);
$this->info("Redis key: {$key}");
$this->info('Value: ' . ($value ?? '(empty)'));
$this->info("TTL: {$ttl}s");
}
}
Show Replace Field
Search History
429...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JY-20725-handle-HS-search-rate-limit, menu","depth":5,"on_screen":true,"help_text":"Git Branch: JY-20725-handle-HS-search-rate-limit","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"HandleHubspotRateLimitTest","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'HandleHubspotRateLimitTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'HandleHubspotRateLimitTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"5","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"133","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"11","depth":4,"on_screen":true,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Console\\Commands;\n\nuse Carbon\\Carbon;\nuse Carbon\\CarbonImmutable;\nuse Illuminate\\Console\\Command;\nuse Illuminate\\Support\\Facades\\Redis;\nuse InvalidArgumentException;\nuse Jiminny\\Jobs\\AutomatedReports\\RequestGenerateAskJiminnyReportJob;\nuse Jiminny\\Jobs\\AutomatedReports\\SendReportMailJob;\nuse Jiminny\\Jobs\\Crm\\Delete\\VerifyActivityCrmTaskJob;\nuse Jiminny\\Jobs\\Crm\\MatchActivityCrmData;\nuse Jiminny\\Jobs\\JobDispatcherInterface;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\AutomatedReportResult;\nuse Jiminny\\Models\\Team;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Repositories\\AutomatedReportsRepository;\nuse Jiminny\\Services\\Activity\\CrmOwnerResolver;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\n\n/**\n * Class JiminnyDebugCommand\n *\n * @package Jiminny\\Console\\Commands\n */\nclass JiminnyDebugCommand extends Command\n{\n public const string FREQUENCY_DAILY = 'daily';\n public const string FREQUENCY_WEEKLY = 'weekly';\n public const string FREQUENCY_MONTHLY = 'monthly';\n public const string FREQUENCY_QUARTERLY = 'quarterly';\n public const string FREQUENCY_ONE_OFF = 'one_off';\n protected $signature = 'jiminny:debug';\n\n public function handle(\n JobDispatcherInterface $jobDispatcher,\n AutomatedReportsService $automatedReportsService,\n AutomatedReportsRepository $automatedReportsRepository,\n UserPilotClient $userPilotClient\n ): void {\n // Choose ONE of the following to run, then comment out the others.\n // 1) Dispatch a storm of MatchActivityCrmData jobs against team 2\n $this->simulateMatchActivityStorm(teamId: 2, count: 100);\n\n // 2) Dispatch a storm of VerifyActivityCrmTaskJob jobs (simulates DeleteCrmEntityTrait fan-out)\n // $this->simulateVerifyTaskStorm(teamId: 2, count: 100);\n\n // 3) Inspect Redis circuit-breaker state for the team's HubSpot portal\n // $this->observeRateLimitCache(teamId: 2);\n\n // 4) Make 3 synchronous matchByName calls (foreground, hits API directly)\n // $this->rateLimit();\n exit(1);\n\n\n\n $report = AutomatedReport::find(71);\n $last = AutomatedReportResult::query()\n ->where('report_id', $report->getId())\n ->whereIn('status', [AutomatedReportResult::STATUS_DEFAULT, AutomatedReportResult::STATUS_FAILED])\n// ->where('reason', '!=', AutomatedReportResult::REASON_NOT_ENOUGH_ACTIVITIES)\n ->whereDate('created_at', CarbonImmutable::now()->toDateString())\n ->latest()\n ->first();\n\n $this->info(\"Last: {$last->getId()}\");\n\n exit(1);\n\n $user = User::find(143);\n // $count = $automatedReportsRepository->countUserReports($user);\n // $this->info(\"Count: {$count}\");\n // $count = $automatedReportsRepository->countAllUserReports($user);\n // $this->info(\"All count: {$count}\");\n\n $payload = [\n 'report_type' => 'ask_jiminny',\n 'frequency' => 'weekly',\n ];\n $userPilotClient->track($user, 'ask-jiminny-report-generated', $payload);\n\n exit(1);\n\n $now = Carbon::now()->subDay(1);\n $this->info(\"Now: {$now->toDateTimeString()}\");\n $weekStart = Carbon::getWeekStartsAt();\n $this->info(\"Now: {$weekStart}\");\n\n // $from = $now->copy()->previousWeekday()->startOfDay();\n // $to = $now->copy()->previousWeekday()->endOfDay();\n\n // $fromOld = $now->copy()->subWeeks(1)->startOfDay();\n // $toOld = $now->copy()->subDay()->endOfDay();\n // $fromNew = $now->copy()->subWeek()->startOfWeek();\n // $toNew = $now->copy()->subWeek()->endOfWeek();\n\n // $fromOld = $now->copy()->subMonths(1)->startOfDay();\n // $toOld = $now->copy()->subDay()->endOfDay();\n // $fromNew = $now->copy()->subMonthNoOverflow()->startOfMonth();\n // $toNew = $now->copy()->subMonthNoOverflow()->endOfMonth();\n\n $fromOld = $now->copy()->subMonths(3)->startOfDay();\n $toOld = $now->copy()->subDay()->endOfDay();\n $fromNew = $now->copy()->subQuarterNoOverflow()->startOfQuarter();\n $toNew = $now->copy()->subQuarterNoOverflow()->endOfQuarter();\n\n $this->info(\"From old: {$fromOld->toDateTimeString()}\");\n $this->info(\"To old: {$toOld->toDateTimeString()}\");\n $this->info(\"From new: {$fromNew->toDateTimeString()}\");\n $this->info(\"To new: {$toNew->toDateTimeString()}\");\n\n exit(1);\n\n $report = AutomatedReport::find(71);\n\n $job = new RequestGenerateAskJiminnyReportJob($report->getUuid());\n $jobDispatcher->dispatch($job);\n\n exit(1);\n\n\n // $this->formatDate($jobDispatcher);\n // $this->sendMail($jobDispatcher, $automatedReportsService);\n // $this->crmService();\n\n $this->getPayload($automatedReportsService);\n\n exit(1);\n }\n\n\n\n private function crmService()\n {\n $activity = Activity::find(418141);\n\n $team = Team::find(19);\n $config = $team->getCrmConfiguration();\n\n $crmResolver = app(CrmOwnerResolver::class, [\n 'team' => $team,\n 'integrationAdmin' => $team->getOwner(),\n 'providerSlug' => $config->getProviderName(),\n ]);\n\n $crmService = $crmResolver->prepareCrmService();\n\n $crmService->createTranscriptNotes($activity);\n }\n\n private function sendMail(JobDispatcherInterface $jobDispatcher, AutomatedReportsService $automatedReportsService)\n {\n $reportUuid = '';\n // $report = $automatedReportsService->getReportResult($reportUuid);\n $report = AutomatedReportResult::find(275);\n $validRecipients = $automatedReportsService->getValidRecipientUsers(\n $report->getReport(),\n includeJiminny: true,\n );\n\n $recipient = $validRecipients[0];\n\n $fileName = $automatedReportsService->getReportFileName($report);\n $typeName = $report->getReport()->getCustomName()\n ?? $automatedReportsService->getReportTypeName($report);\n $teamsName = $automatedReportsService->getReportTeamsName($report);\n $periodName = $automatedReportsService->getReportPeriodName($report);\n $s3Path = $automatedReportsService->getMediaPath($report);\n\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$fileName ' . PHP_EOL . print_r($fileName, true));\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$typeName ' . PHP_EOL . print_r($typeName, true));\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$teamsName ' . PHP_EOL . print_r($teamsName, true));\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$periodName ' . PHP_EOL . print_r($periodName, true));\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$s3Path ' . PHP_EOL . print_r($s3Path, true));\n\n $jobDispatcher->dispatch(\n new SendReportMailJob(\n reportUuid: $report->getUuid(),\n s3Path: $s3Path,\n recipientEmail: $recipient['email'],\n recipientName: $recipient['name'] ?? null,\n fileName: $fileName,\n typeName: $typeName,\n teamsName: $teamsName,\n periodName: $periodName,\n isAskJiminny: true,\n )\n );\n\n exit(1);\n }\n\n private function formatDate(JobDispatcherInterface $jobDispatcher): void\n {\n $customName = 'Custom report name';\n // $frequency = self::FREQUENCY_DAILY;\n // $frequency = self::FREQUENCY_WEEKLY;\n $frequency = self::FREQUENCY_MONTHLY;\n // $frequency = self::FREQUENCY_QUARTERLY;\n // $frequency = self::FREQUENCY_ONE_OFF;\n $period = $this->calculateFromAndToDatePeriod($frequency);\n $from = $period['fromDate'];\n $to = $period['toDate'];\n $periodName = $this->formatReportPeriodName($frequency, $from, $to);\n $filenameSuffix = null;\n\n if ($customName) {\n if ($filenameSuffix) {\n $customName .= \" {$filenameSuffix}\";\n }\n\n $result = $this->sanitizeFileName(\"{$customName} - {$periodName}\");\n }\n\n $this->info($result);\n }\n\n public function calculateFromAndToDatePeriod(\n string $frequency,\n ?Carbon $fromDate = null,\n ?Carbon $toDate = null\n ): array {\n if ($frequency === self::FREQUENCY_ONE_OFF) {\n return [\n 'fromDate' => $fromDate,\n 'toDate' => $toDate,\n ];\n }\n\n $now = Carbon::now();\n\n return match ($frequency) {\n self::FREQUENCY_DAILY => [\n 'fromDate' => $now->copy()->subDay()->startOfDay(),\n 'toDate' => $now->copy()->subDay()->endOfDay(),\n ],\n self::FREQUENCY_WEEKLY => [\n 'fromDate' => $now->copy()->subWeeks(1)->startOfDay(),\n 'toDate' => $now->copy()->subDay()->endOfDay(),\n ],\n self::FREQUENCY_MONTHLY => [\n 'fromDate' => $now->copy()->subMonths(1)->startOfDay(),\n 'toDate' => $now->copy()->subDay()->endOfDay(),\n ],\n self::FREQUENCY_QUARTERLY => [\n 'fromDate' => $now->copy()->subMonths(3)->startOfDay(),\n 'toDate' => $now->copy()->subDay()->endOfDay(),\n ],\n default => throw new InvalidArgumentException(\"Unsupported frequency: {$frequency}\"),\n };\n }\n\n private function formatReportPeriodName(string $frequency, Carbon $from, Carbon $to): string\n {\n $fromYear = $from->format('Y');\n $toYear = $to->format('Y');\n $differentYears = $fromYear !== $toYear;\n\n switch ($frequency) {\n case self::FREQUENCY_DAILY:\n return $from->format('j M Y');\n\n case self::FREQUENCY_QUARTERLY:\n // 'Jan-Mar 2025' or 'Nov 2024-Jan 2025' if years differ\n $startMonth = $from->format('M');\n $endMonth = $to->copy()->subMonth();\n $endMonthName = $endMonth->format('M');\n $endMonthYear = $endMonth->format('Y');\n\n if ($differentYears) {\n return \"{$startMonth} {$fromYear} - {$endMonthName} {$endMonthYear}\";\n }\n\n return \"{$startMonth} - {$endMonthName} {$toYear}\";\n\n case self::FREQUENCY_MONTHLY:\n // 'May 2025' - monthly reports are always within the same year\n return $from->format('M Y');\n\n case self::FREQUENCY_WEEKLY:\n // '4 - 8 Aug 2025', '27 Oct - 3 Nov 2025', or '28 Dec 2024 - 3 Jan 2025' if years differ\n $startDay = $from->format('j');\n $endDay = $to->format('j');\n $startMonth = $from->format('M');\n $endMonth = $to->format('M');\n\n if ($differentYears) {\n return \"{$startDay} {$startMonth} {$fromYear} - {$endDay} {$endMonth} {$toYear}\";\n }\n\n if ($startMonth !== $endMonth) {\n return \"{$startDay} {$startMonth} - {$endDay} {$endMonth} {$toYear}\";\n }\n\n return \"{$startDay} - {$endDay} {$endMonth} {$toYear}\";\n\n case self::FREQUENCY_ONE_OFF:\n // '2 May-31 May 2025' or '15 Dec 2024-15 Jan 2025' if years differ\n $startDay = $from->format('j');\n $startMonth = $from->format('M');\n $endDay = $to->format('j');\n $endMonth = $to->format('M');\n\n // If same month and year, use a format like '2-31 May 2025'\n if ($startMonth === $endMonth && ! $differentYears) {\n return \"{$startDay} - {$endDay} {$startMonth} {$toYear}\";\n }\n\n // If different years, include both years\n if ($differentYears) {\n return \"{$startDay} {$startMonth} {$fromYear} - {$endDay} {$endMonth} {$toYear}\";\n }\n\n // Same year but different months\n return \"{$startDay} {$startMonth} - {$endDay} {$endMonth} {$toYear}\";\n\n default:\n // Default format for unknown frequencies\n return $from->format('j M Y') . ' - ' . $to->format('j M Y');\n }\n }\n\n public function sanitizeFileName(string $fileName): string\n {\n return str_replace(['/', '\\\\'], '-', $fileName);\n }\n\n private function getPayload(AutomatedReportsService $automatedReportsService)\n {\n $reportResult = AutomatedReportResult::find(269);\n $automatedReport = $reportResult->getReport();\n $activityIds = [1,2,3];\n $payload = $automatedReportsService->getAskJiminnyGenerateReportPayload(\n automatedReport: $automatedReport,\n reportResult: $reportResult,\n activityIds: $activityIds,\n );\n\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$payload ' . PHP_EOL . print_r($payload, true));\n }\n\n private function rateLimit()\n {\n $team = Team::find(2);\n $config = $team->getCrmConfiguration();\n\n $crmResolver = app(CrmOwnerResolver::class, [\n 'team' => $team,\n 'integrationAdmin' => $team->getOwner(),\n 'providerSlug' => $config->getProviderName(),\n ]);\n\n $crmService = $crmResolver->prepareCrmService();\n\n for ($i = 0 ; $i < 3; $i++) {\n// if ($i % 25 === 0) {\n// $this->info(\"Syncing opportunity {$i}\");\n $this->info(\"Matching contact {$i}\");\n// }\n// $crmService->syncOpportunity('374720564');\n $crmService->matchByName('Robot');\n }\n }\n\n private function simulateMatchActivityStorm(int $teamId = 2, int $count = 100): void\n {\n $team = Team::find($teamId);\n $config = $team->getCrmConfiguration();\n\n $activities = Activity::query()\n ->where('crm_configuration_id', $config->getId())\n ->orderByDesc('id')\n ->limit($count)\n ->get();\n\n $this->info(\"Dispatching {$activities->count()} MatchActivityCrmData jobs (portal={$config->getId()})\");\n\n foreach ($activities as $activity) {\n MatchActivityCrmData::dispatch($activity->getId(), $config, true);\n }\n\n $this->info('Done. Watch logs and run jiminny:debug observeRateLimit to inspect cache state.');\n }\n\n private function simulateVerifyTaskStorm(int $teamId = 2, int $count = 100): void\n {\n $activities = Activity::query()\n ->where('team_id', $teamId)\n ->whereNotNull('crm_provider_id')\n ->orderByDesc('id')\n ->limit($count)\n ->get();\n\n $this->info(\"Dispatching {$activities->count()} VerifyActivityCrmTaskJob jobs\");\n\n foreach ($activities as $activity) {\n VerifyActivityCrmTaskJob::dispatch($activity->getId());\n }\n\n $this->info('Done.');\n }\n\n private function observeRateLimitCache(int $teamId = 2): void\n {\n $team = Team::find($teamId);\n $config = $team->getCrmConfiguration();\n $key = sprintf('hubspot:ratelimit:portal:%d', $config->getId());\n\n $value = Redis::get($key);\n $ttl = Redis::ttl($key);\n\n $this->info(\"Redis key: {$key}\");\n $this->info('Value: ' . ($value ?? '(empty)'));\n $this->info(\"TTL: {$ttl}s\");\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Console\\Commands;\n\nuse Carbon\\Carbon;\nuse Carbon\\CarbonImmutable;\nuse Illuminate\\Console\\Command;\nuse Illuminate\\Support\\Facades\\Redis;\nuse InvalidArgumentException;\nuse Jiminny\\Jobs\\AutomatedReports\\RequestGenerateAskJiminnyReportJob;\nuse Jiminny\\Jobs\\AutomatedReports\\SendReportMailJob;\nuse Jiminny\\Jobs\\Crm\\Delete\\VerifyActivityCrmTaskJob;\nuse Jiminny\\Jobs\\Crm\\MatchActivityCrmData;\nuse Jiminny\\Jobs\\JobDispatcherInterface;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\AutomatedReportResult;\nuse Jiminny\\Models\\Team;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Repositories\\AutomatedReportsRepository;\nuse Jiminny\\Services\\Activity\\CrmOwnerResolver;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\n\n/**\n * Class JiminnyDebugCommand\n *\n * @package Jiminny\\Console\\Commands\n */\nclass JiminnyDebugCommand extends Command\n{\n public const string FREQUENCY_DAILY = 'daily';\n public const string FREQUENCY_WEEKLY = 'weekly';\n public const string FREQUENCY_MONTHLY = 'monthly';\n public const string FREQUENCY_QUARTERLY = 'quarterly';\n public const string FREQUENCY_ONE_OFF = 'one_off';\n protected $signature = 'jiminny:debug';\n\n public function handle(\n JobDispatcherInterface $jobDispatcher,\n AutomatedReportsService $automatedReportsService,\n AutomatedReportsRepository $automatedReportsRepository,\n UserPilotClient $userPilotClient\n ): void {\n // Choose ONE of the following to run, then comment out the others.\n // 1) Dispatch a storm of MatchActivityCrmData jobs against team 2\n $this->simulateMatchActivityStorm(teamId: 2, count: 100);\n\n // 2) Dispatch a storm of VerifyActivityCrmTaskJob jobs (simulates DeleteCrmEntityTrait fan-out)\n // $this->simulateVerifyTaskStorm(teamId: 2, count: 100);\n\n // 3) Inspect Redis circuit-breaker state for the team's HubSpot portal\n // $this->observeRateLimitCache(teamId: 2);\n\n // 4) Make 3 synchronous matchByName calls (foreground, hits API directly)\n // $this->rateLimit();\n exit(1);\n\n\n\n $report = AutomatedReport::find(71);\n $last = AutomatedReportResult::query()\n ->where('report_id', $report->getId())\n ->whereIn('status', [AutomatedReportResult::STATUS_DEFAULT, AutomatedReportResult::STATUS_FAILED])\n// ->where('reason', '!=', AutomatedReportResult::REASON_NOT_ENOUGH_ACTIVITIES)\n ->whereDate('created_at', CarbonImmutable::now()->toDateString())\n ->latest()\n ->first();\n\n $this->info(\"Last: {$last->getId()}\");\n\n exit(1);\n\n $user = User::find(143);\n // $count = $automatedReportsRepository->countUserReports($user);\n // $this->info(\"Count: {$count}\");\n // $count = $automatedReportsRepository->countAllUserReports($user);\n // $this->info(\"All count: {$count}\");\n\n $payload = [\n 'report_type' => 'ask_jiminny',\n 'frequency' => 'weekly',\n ];\n $userPilotClient->track($user, 'ask-jiminny-report-generated', $payload);\n\n exit(1);\n\n $now = Carbon::now()->subDay(1);\n $this->info(\"Now: {$now->toDateTimeString()}\");\n $weekStart = Carbon::getWeekStartsAt();\n $this->info(\"Now: {$weekStart}\");\n\n // $from = $now->copy()->previousWeekday()->startOfDay();\n // $to = $now->copy()->previousWeekday()->endOfDay();\n\n // $fromOld = $now->copy()->subWeeks(1)->startOfDay();\n // $toOld = $now->copy()->subDay()->endOfDay();\n // $fromNew = $now->copy()->subWeek()->startOfWeek();\n // $toNew = $now->copy()->subWeek()->endOfWeek();\n\n // $fromOld = $now->copy()->subMonths(1)->startOfDay();\n // $toOld = $now->copy()->subDay()->endOfDay();\n // $fromNew = $now->copy()->subMonthNoOverflow()->startOfMonth();\n // $toNew = $now->copy()->subMonthNoOverflow()->endOfMonth();\n\n $fromOld = $now->copy()->subMonths(3)->startOfDay();\n $toOld = $now->copy()->subDay()->endOfDay();\n $fromNew = $now->copy()->subQuarterNoOverflow()->startOfQuarter();\n $toNew = $now->copy()->subQuarterNoOverflow()->endOfQuarter();\n\n $this->info(\"From old: {$fromOld->toDateTimeString()}\");\n $this->info(\"To old: {$toOld->toDateTimeString()}\");\n $this->info(\"From new: {$fromNew->toDateTimeString()}\");\n $this->info(\"To new: {$toNew->toDateTimeString()}\");\n\n exit(1);\n\n $report = AutomatedReport::find(71);\n\n $job = new RequestGenerateAskJiminnyReportJob($report->getUuid());\n $jobDispatcher->dispatch($job);\n\n exit(1);\n\n\n // $this->formatDate($jobDispatcher);\n // $this->sendMail($jobDispatcher, $automatedReportsService);\n // $this->crmService();\n\n $this->getPayload($automatedReportsService);\n\n exit(1);\n }\n\n\n\n private function crmService()\n {\n $activity = Activity::find(418141);\n\n $team = Team::find(19);\n $config = $team->getCrmConfiguration();\n\n $crmResolver = app(CrmOwnerResolver::class, [\n 'team' => $team,\n 'integrationAdmin' => $team->getOwner(),\n 'providerSlug' => $config->getProviderName(),\n ]);\n\n $crmService = $crmResolver->prepareCrmService();\n\n $crmService->createTranscriptNotes($activity);\n }\n\n private function sendMail(JobDispatcherInterface $jobDispatcher, AutomatedReportsService $automatedReportsService)\n {\n $reportUuid = '';\n // $report = $automatedReportsService->getReportResult($reportUuid);\n $report = AutomatedReportResult::find(275);\n $validRecipients = $automatedReportsService->getValidRecipientUsers(\n $report->getReport(),\n includeJiminny: true,\n );\n\n $recipient = $validRecipients[0];\n\n $fileName = $automatedReportsService->getReportFileName($report);\n $typeName = $report->getReport()->getCustomName()\n ?? $automatedReportsService->getReportTypeName($report);\n $teamsName = $automatedReportsService->getReportTeamsName($report);\n $periodName = $automatedReportsService->getReportPeriodName($report);\n $s3Path = $automatedReportsService->getMediaPath($report);\n\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$fileName ' . PHP_EOL . print_r($fileName, true));\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$typeName ' . PHP_EOL . print_r($typeName, true));\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$teamsName ' . PHP_EOL . print_r($teamsName, true));\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$periodName ' . PHP_EOL . print_r($periodName, true));\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$s3Path ' . PHP_EOL . print_r($s3Path, true));\n\n $jobDispatcher->dispatch(\n new SendReportMailJob(\n reportUuid: $report->getUuid(),\n s3Path: $s3Path,\n recipientEmail: $recipient['email'],\n recipientName: $recipient['name'] ?? null,\n fileName: $fileName,\n typeName: $typeName,\n teamsName: $teamsName,\n periodName: $periodName,\n isAskJiminny: true,\n )\n );\n\n exit(1);\n }\n\n private function formatDate(JobDispatcherInterface $jobDispatcher): void\n {\n $customName = 'Custom report name';\n // $frequency = self::FREQUENCY_DAILY;\n // $frequency = self::FREQUENCY_WEEKLY;\n $frequency = self::FREQUENCY_MONTHLY;\n // $frequency = self::FREQUENCY_QUARTERLY;\n // $frequency = self::FREQUENCY_ONE_OFF;\n $period = $this->calculateFromAndToDatePeriod($frequency);\n $from = $period['fromDate'];\n $to = $period['toDate'];\n $periodName = $this->formatReportPeriodName($frequency, $from, $to);\n $filenameSuffix = null;\n\n if ($customName) {\n if ($filenameSuffix) {\n $customName .= \" {$filenameSuffix}\";\n }\n\n $result = $this->sanitizeFileName(\"{$customName} - {$periodName}\");\n }\n\n $this->info($result);\n }\n\n public function calculateFromAndToDatePeriod(\n string $frequency,\n ?Carbon $fromDate = null,\n ?Carbon $toDate = null\n ): array {\n if ($frequency === self::FREQUENCY_ONE_OFF) {\n return [\n 'fromDate' => $fromDate,\n 'toDate' => $toDate,\n ];\n }\n\n $now = Carbon::now();\n\n return match ($frequency) {\n self::FREQUENCY_DAILY => [\n 'fromDate' => $now->copy()->subDay()->startOfDay(),\n 'toDate' => $now->copy()->subDay()->endOfDay(),\n ],\n self::FREQUENCY_WEEKLY => [\n 'fromDate' => $now->copy()->subWeeks(1)->startOfDay(),\n 'toDate' => $now->copy()->subDay()->endOfDay(),\n ],\n self::FREQUENCY_MONTHLY => [\n 'fromDate' => $now->copy()->subMonths(1)->startOfDay(),\n 'toDate' => $now->copy()->subDay()->endOfDay(),\n ],\n self::FREQUENCY_QUARTERLY => [\n 'fromDate' => $now->copy()->subMonths(3)->startOfDay(),\n 'toDate' => $now->copy()->subDay()->endOfDay(),\n ],\n default => throw new InvalidArgumentException(\"Unsupported frequency: {$frequency}\"),\n };\n }\n\n private function formatReportPeriodName(string $frequency, Carbon $from, Carbon $to): string\n {\n $fromYear = $from->format('Y');\n $toYear = $to->format('Y');\n $differentYears = $fromYear !== $toYear;\n\n switch ($frequency) {\n case self::FREQUENCY_DAILY:\n return $from->format('j M Y');\n\n case self::FREQUENCY_QUARTERLY:\n // 'Jan-Mar 2025' or 'Nov 2024-Jan 2025' if years differ\n $startMonth = $from->format('M');\n $endMonth = $to->copy()->subMonth();\n $endMonthName = $endMonth->format('M');\n $endMonthYear = $endMonth->format('Y');\n\n if ($differentYears) {\n return \"{$startMonth} {$fromYear} - {$endMonthName} {$endMonthYear}\";\n }\n\n return \"{$startMonth} - {$endMonthName} {$toYear}\";\n\n case self::FREQUENCY_MONTHLY:\n // 'May 2025' - monthly reports are always within the same year\n return $from->format('M Y');\n\n case self::FREQUENCY_WEEKLY:\n // '4 - 8 Aug 2025', '27 Oct - 3 Nov 2025', or '28 Dec 2024 - 3 Jan 2025' if years differ\n $startDay = $from->format('j');\n $endDay = $to->format('j');\n $startMonth = $from->format('M');\n $endMonth = $to->format('M');\n\n if ($differentYears) {\n return \"{$startDay} {$startMonth} {$fromYear} - {$endDay} {$endMonth} {$toYear}\";\n }\n\n if ($startMonth !== $endMonth) {\n return \"{$startDay} {$startMonth} - {$endDay} {$endMonth} {$toYear}\";\n }\n\n return \"{$startDay} - {$endDay} {$endMonth} {$toYear}\";\n\n case self::FREQUENCY_ONE_OFF:\n // '2 May-31 May 2025' or '15 Dec 2024-15 Jan 2025' if years differ\n $startDay = $from->format('j');\n $startMonth = $from->format('M');\n $endDay = $to->format('j');\n $endMonth = $to->format('M');\n\n // If same month and year, use a format like '2-31 May 2025'\n if ($startMonth === $endMonth && ! $differentYears) {\n return \"{$startDay} - {$endDay} {$startMonth} {$toYear}\";\n }\n\n // If different years, include both years\n if ($differentYears) {\n return \"{$startDay} {$startMonth} {$fromYear} - {$endDay} {$endMonth} {$toYear}\";\n }\n\n // Same year but different months\n return \"{$startDay} {$startMonth} - {$endDay} {$endMonth} {$toYear}\";\n\n default:\n // Default format for unknown frequencies\n return $from->format('j M Y') . ' - ' . $to->format('j M Y');\n }\n }\n\n public function sanitizeFileName(string $fileName): string\n {\n return str_replace(['/', '\\\\'], '-', $fileName);\n }\n\n private function getPayload(AutomatedReportsService $automatedReportsService)\n {\n $reportResult = AutomatedReportResult::find(269);\n $automatedReport = $reportResult->getReport();\n $activityIds = [1,2,3];\n $payload = $automatedReportsService->getAskJiminnyGenerateReportPayload(\n automatedReport: $automatedReport,\n reportResult: $reportResult,\n activityIds: $activityIds,\n );\n\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$payload ' . PHP_EOL . print_r($payload, true));\n }\n\n private function rateLimit()\n {\n $team = Team::find(2);\n $config = $team->getCrmConfiguration();\n\n $crmResolver = app(CrmOwnerResolver::class, [\n 'team' => $team,\n 'integrationAdmin' => $team->getOwner(),\n 'providerSlug' => $config->getProviderName(),\n ]);\n\n $crmService = $crmResolver->prepareCrmService();\n\n for ($i = 0 ; $i < 3; $i++) {\n// if ($i % 25 === 0) {\n// $this->info(\"Syncing opportunity {$i}\");\n $this->info(\"Matching contact {$i}\");\n// }\n// $crmService->syncOpportunity('374720564');\n $crmService->matchByName('Robot');\n }\n }\n\n private function simulateMatchActivityStorm(int $teamId = 2, int $count = 100): void\n {\n $team = Team::find($teamId);\n $config = $team->getCrmConfiguration();\n\n $activities = Activity::query()\n ->where('crm_configuration_id', $config->getId())\n ->orderByDesc('id')\n ->limit($count)\n ->get();\n\n $this->info(\"Dispatching {$activities->count()} MatchActivityCrmData jobs (portal={$config->getId()})\");\n\n foreach ($activities as $activity) {\n MatchActivityCrmData::dispatch($activity->getId(), $config, true);\n }\n\n $this->info('Done. Watch logs and run jiminny:debug observeRateLimit to inspect cache state.');\n }\n\n private function simulateVerifyTaskStorm(int $teamId = 2, int $count = 100): void\n {\n $activities = Activity::query()\n ->where('team_id', $teamId)\n ->whereNotNull('crm_provider_id')\n ->orderByDesc('id')\n ->limit($count)\n ->get();\n\n $this->info(\"Dispatching {$activities->count()} VerifyActivityCrmTaskJob jobs\");\n\n foreach ($activities as $activity) {\n VerifyActivityCrmTaskJob::dispatch($activity->getId());\n }\n\n $this->info('Done.');\n }\n\n private function observeRateLimitCache(int $teamId = 2): void\n {\n $team = Team::find($teamId);\n $config = $team->getCrmConfiguration();\n $key = sprintf('hubspot:ratelimit:portal:%d', $config->getId());\n\n $value = Redis::get($key);\n $ttl = Redis::ttl($key);\n\n $this->info(\"Redis key: {$key}\");\n $this->info('Value: ' . ($value ?? '(empty)'));\n $this->info(\"TTL: {$ttl}s\");\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Show Replace Field","depth":4,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Search History","depth":3,"on_screen":true,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"429","depth":4,"on_screen":true,"value":"429","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
1209453926218787028
|
3603276678078998955
|
click
|
accessibility
|
NULL
|
Project: faVsco.js, menu
JY-20725-handle-HS-search Project: faVsco.js, menu
JY-20725-handle-HS-search-rate-limit, menu
Start Listening for PHP Debug Connections
HandleHubspotRateLimitTest
Run 'HandleHubspotRateLimitTest'
Debug 'HandleHubspotRateLimitTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
5
133
11
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Console\Commands;
use Carbon\Carbon;
use Carbon\CarbonImmutable;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Redis;
use InvalidArgumentException;
use Jiminny\Jobs\AutomatedReports\RequestGenerateAskJiminnyReportJob;
use Jiminny\Jobs\AutomatedReports\SendReportMailJob;
use Jiminny\Jobs\Crm\Delete\VerifyActivityCrmTaskJob;
use Jiminny\Jobs\Crm\MatchActivityCrmData;
use Jiminny\Jobs\JobDispatcherInterface;
use Jiminny\Models\Activity;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\AutomatedReportResult;
use Jiminny\Models\Team;
use Jiminny\Models\User;
use Jiminny\Repositories\AutomatedReportsRepository;
use Jiminny\Services\Activity\CrmOwnerResolver;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
/**
* Class JiminnyDebugCommand
*
* @package Jiminny\Console\Commands
*/
class JiminnyDebugCommand extends Command
{
public const string FREQUENCY_DAILY = 'daily';
public const string FREQUENCY_WEEKLY = 'weekly';
public const string FREQUENCY_MONTHLY = 'monthly';
public const string FREQUENCY_QUARTERLY = 'quarterly';
public const string FREQUENCY_ONE_OFF = 'one_off';
protected $signature = 'jiminny:debug';
public function handle(
JobDispatcherInterface $jobDispatcher,
AutomatedReportsService $automatedReportsService,
AutomatedReportsRepository $automatedReportsRepository,
UserPilotClient $userPilotClient
): void {
// Choose ONE of the following to run, then comment out the others.
// 1) Dispatch a storm of MatchActivityCrmData jobs against team 2
$this->simulateMatchActivityStorm(teamId: 2, count: 100);
// 2) Dispatch a storm of VerifyActivityCrmTaskJob jobs (simulates DeleteCrmEntityTrait fan-out)
// $this->simulateVerifyTaskStorm(teamId: 2, count: 100);
// 3) Inspect Redis circuit-breaker state for the team's HubSpot portal
// $this->observeRateLimitCache(teamId: 2);
// 4) Make 3 synchronous matchByName calls (foreground, hits API directly)
// $this->rateLimit();
exit(1);
$report = AutomatedReport::find(71);
$last = AutomatedReportResult::query()
->where('report_id', $report->getId())
->whereIn('status', [AutomatedReportResult::STATUS_DEFAULT, AutomatedReportResult::STATUS_FAILED])
// ->where('reason', '!=', AutomatedReportResult::REASON_NOT_ENOUGH_ACTIVITIES)
->whereDate('created_at', CarbonImmutable::now()->toDateString())
->latest()
->first();
$this->info("Last: {$last->getId()}");
exit(1);
$user = User::find(143);
// $count = $automatedReportsRepository->countUserReports($user);
// $this->info("Count: {$count}");
// $count = $automatedReportsRepository->countAllUserReports($user);
// $this->info("All count: {$count}");
$payload = [
'report_type' => 'ask_jiminny',
'frequency' => 'weekly',
];
$userPilotClient->track($user, 'ask-jiminny-report-generated', $payload);
exit(1);
$now = Carbon::now()->subDay(1);
$this->info("Now: {$now->toDateTimeString()}");
$weekStart = Carbon::getWeekStartsAt();
$this->info("Now: {$weekStart}");
// $from = $now->copy()->previousWeekday()->startOfDay();
// $to = $now->copy()->previousWeekday()->endOfDay();
// $fromOld = $now->copy()->subWeeks(1)->startOfDay();
// $toOld = $now->copy()->subDay()->endOfDay();
// $fromNew = $now->copy()->subWeek()->startOfWeek();
// $toNew = $now->copy()->subWeek()->endOfWeek();
// $fromOld = $now->copy()->subMonths(1)->startOfDay();
// $toOld = $now->copy()->subDay()->endOfDay();
// $fromNew = $now->copy()->subMonthNoOverflow()->startOfMonth();
// $toNew = $now->copy()->subMonthNoOverflow()->endOfMonth();
$fromOld = $now->copy()->subMonths(3)->startOfDay();
$toOld = $now->copy()->subDay()->endOfDay();
$fromNew = $now->copy()->subQuarterNoOverflow()->startOfQuarter();
$toNew = $now->copy()->subQuarterNoOverflow()->endOfQuarter();
$this->info("From old: {$fromOld->toDateTimeString()}");
$this->info("To old: {$toOld->toDateTimeString()}");
$this->info("From new: {$fromNew->toDateTimeString()}");
$this->info("To new: {$toNew->toDateTimeString()}");
exit(1);
$report = AutomatedReport::find(71);
$job = new RequestGenerateAskJiminnyReportJob($report->getUuid());
$jobDispatcher->dispatch($job);
exit(1);
// $this->formatDate($jobDispatcher);
// $this->sendMail($jobDispatcher, $automatedReportsService);
// $this->crmService();
$this->getPayload($automatedReportsService);
exit(1);
}
private function crmService()
{
$activity = Activity::find(418141);
$team = Team::find(19);
$config = $team->getCrmConfiguration();
$crmResolver = app(CrmOwnerResolver::class, [
'team' => $team,
'integrationAdmin' => $team->getOwner(),
'providerSlug' => $config->getProviderName(),
]);
$crmService = $crmResolver->prepareCrmService();
$crmService->createTranscriptNotes($activity);
}
private function sendMail(JobDispatcherInterface $jobDispatcher, AutomatedReportsService $automatedReportsService)
{
$reportUuid = '';
// $report = $automatedReportsService->getReportResult($reportUuid);
$report = AutomatedReportResult::find(275);
$validRecipients = $automatedReportsService->getValidRecipientUsers(
$report->getReport(),
includeJiminny: true,
);
$recipient = $validRecipients[0];
$fileName = $automatedReportsService->getReportFileName($report);
$typeName = $report->getReport()->getCustomName()
?? $automatedReportsService->getReportTypeName($report);
$teamsName = $automatedReportsService->getReportTeamsName($report);
$periodName = $automatedReportsService->getReportPeriodName($report);
$s3Path = $automatedReportsService->getMediaPath($report);
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$fileName ' . PHP_EOL . print_r($fileName, true));
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$typeName ' . PHP_EOL . print_r($typeName, true));
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$teamsName ' . PHP_EOL . print_r($teamsName, true));
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$periodName ' . PHP_EOL . print_r($periodName, true));
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$s3Path ' . PHP_EOL . print_r($s3Path, true));
$jobDispatcher->dispatch(
new SendReportMailJob(
reportUuid: $report->getUuid(),
s3Path: $s3Path,
recipientEmail: $recipient['email'],
recipientName: $recipient['name'] ?? null,
fileName: $fileName,
typeName: $typeName,
teamsName: $teamsName,
periodName: $periodName,
isAskJiminny: true,
)
);
exit(1);
}
private function formatDate(JobDispatcherInterface $jobDispatcher): void
{
$customName = 'Custom report name';
// $frequency = self::FREQUENCY_DAILY;
// $frequency = self::FREQUENCY_WEEKLY;
$frequency = self::FREQUENCY_MONTHLY;
// $frequency = self::FREQUENCY_QUARTERLY;
// $frequency = self::FREQUENCY_ONE_OFF;
$period = $this->calculateFromAndToDatePeriod($frequency);
$from = $period['fromDate'];
$to = $period['toDate'];
$periodName = $this->formatReportPeriodName($frequency, $from, $to);
$filenameSuffix = null;
if ($customName) {
if ($filenameSuffix) {
$customName .= " {$filenameSuffix}";
}
$result = $this->sanitizeFileName("{$customName} - {$periodName}");
}
$this->info($result);
}
public function calculateFromAndToDatePeriod(
string $frequency,
?Carbon $fromDate = null,
?Carbon $toDate = null
): array {
if ($frequency === self::FREQUENCY_ONE_OFF) {
return [
'fromDate' => $fromDate,
'toDate' => $toDate,
];
}
$now = Carbon::now();
return match ($frequency) {
self::FREQUENCY_DAILY => [
'fromDate' => $now->copy()->subDay()->startOfDay(),
'toDate' => $now->copy()->subDay()->endOfDay(),
],
self::FREQUENCY_WEEKLY => [
'fromDate' => $now->copy()->subWeeks(1)->startOfDay(),
'toDate' => $now->copy()->subDay()->endOfDay(),
],
self::FREQUENCY_MONTHLY => [
'fromDate' => $now->copy()->subMonths(1)->startOfDay(),
'toDate' => $now->copy()->subDay()->endOfDay(),
],
self::FREQUENCY_QUARTERLY => [
'fromDate' => $now->copy()->subMonths(3)->startOfDay(),
'toDate' => $now->copy()->subDay()->endOfDay(),
],
default => throw new InvalidArgumentException("Unsupported frequency: {$frequency}"),
};
}
private function formatReportPeriodName(string $frequency, Carbon $from, Carbon $to): string
{
$fromYear = $from->format('Y');
$toYear = $to->format('Y');
$differentYears = $fromYear !== $toYear;
switch ($frequency) {
case self::FREQUENCY_DAILY:
return $from->format('j M Y');
case self::FREQUENCY_QUARTERLY:
// 'Jan-Mar 2025' or 'Nov 2024-Jan 2025' if years differ
$startMonth = $from->format('M');
$endMonth = $to->copy()->subMonth();
$endMonthName = $endMonth->format('M');
$endMonthYear = $endMonth->format('Y');
if ($differentYears) {
return "{$startMonth} {$fromYear} - {$endMonthName} {$endMonthYear}";
}
return "{$startMonth} - {$endMonthName} {$toYear}";
case self::FREQUENCY_MONTHLY:
// 'May 2025' - monthly reports are always within the same year
return $from->format('M Y');
case self::FREQUENCY_WEEKLY:
// '4 - 8 Aug 2025', '27 Oct - 3 Nov 2025', or '28 Dec 2024 - 3 Jan 2025' if years differ
$startDay = $from->format('j');
$endDay = $to->format('j');
$startMonth = $from->format('M');
$endMonth = $to->format('M');
if ($differentYears) {
return "{$startDay} {$startMonth} {$fromYear} - {$endDay} {$endMonth} {$toYear}";
}
if ($startMonth !== $endMonth) {
return "{$startDay} {$startMonth} - {$endDay} {$endMonth} {$toYear}";
}
return "{$startDay} - {$endDay} {$endMonth} {$toYear}";
case self::FREQUENCY_ONE_OFF:
// '2 May-31 May 2025' or '15 Dec 2024-15 Jan 2025' if years differ
$startDay = $from->format('j');
$startMonth = $from->format('M');
$endDay = $to->format('j');
$endMonth = $to->format('M');
// If same month and year, use a format like '2-31 May 2025'
if ($startMonth === $endMonth && ! $differentYears) {
return "{$startDay} - {$endDay} {$startMonth} {$toYear}";
}
// If different years, include both years
if ($differentYears) {
return "{$startDay} {$startMonth} {$fromYear} - {$endDay} {$endMonth} {$toYear}";
}
// Same year but different months
return "{$startDay} {$startMonth} - {$endDay} {$endMonth} {$toYear}";
default:
// Default format for unknown frequencies
return $from->format('j M Y') . ' - ' . $to->format('j M Y');
}
}
public function sanitizeFileName(string $fileName): string
{
return str_replace(['/', '\\'], '-', $fileName);
}
private function getPayload(AutomatedReportsService $automatedReportsService)
{
$reportResult = AutomatedReportResult::find(269);
$automatedReport = $reportResult->getReport();
$activityIds = [1,2,3];
$payload = $automatedReportsService->getAskJiminnyGenerateReportPayload(
automatedReport: $automatedReport,
reportResult: $reportResult,
activityIds: $activityIds,
);
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$payload ' . PHP_EOL . print_r($payload, true));
}
private function rateLimit()
{
$team = Team::find(2);
$config = $team->getCrmConfiguration();
$crmResolver = app(CrmOwnerResolver::class, [
'team' => $team,
'integrationAdmin' => $team->getOwner(),
'providerSlug' => $config->getProviderName(),
]);
$crmService = $crmResolver->prepareCrmService();
for ($i = 0 ; $i < 3; $i++) {
// if ($i % 25 === 0) {
// $this->info("Syncing opportunity {$i}");
$this->info("Matching contact {$i}");
// }
// $crmService->syncOpportunity('374720564');
$crmService->matchByName('Robot');
}
}
private function simulateMatchActivityStorm(int $teamId = 2, int $count = 100): void
{
$team = Team::find($teamId);
$config = $team->getCrmConfiguration();
$activities = Activity::query()
->where('crm_configuration_id', $config->getId())
->orderByDesc('id')
->limit($count)
->get();
$this->info("Dispatching {$activities->count()} MatchActivityCrmData jobs (portal={$config->getId()})");
foreach ($activities as $activity) {
MatchActivityCrmData::dispatch($activity->getId(), $config, true);
}
$this->info('Done. Watch logs and run jiminny:debug observeRateLimit to inspect cache state.');
}
private function simulateVerifyTaskStorm(int $teamId = 2, int $count = 100): void
{
$activities = Activity::query()
->where('team_id', $teamId)
->whereNotNull('crm_provider_id')
->orderByDesc('id')
->limit($count)
->get();
$this->info("Dispatching {$activities->count()} VerifyActivityCrmTaskJob jobs");
foreach ($activities as $activity) {
VerifyActivityCrmTaskJob::dispatch($activity->getId());
}
$this->info('Done.');
}
private function observeRateLimitCache(int $teamId = 2): void
{
$team = Team::find($teamId);
$config = $team->getCrmConfiguration();
$key = sprintf('hubspot:ratelimit:portal:%d', $config->getId());
$value = Redis::get($key);
$ttl = Redis::ttl($key);
$this->info("Redis key: {$key}");
$this->info('Value: ' . ($value ?? '(empty)'));
$this->info("TTL: {$ttl}s");
}
}
Show Replace Field
Search History
429...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
18246
|
791
|
29
|
2026-05-11T11:06:51.674090+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-11/1778 /Users/lukas/.screenpipe/data/data/2026-05-11/1778497611674_m2.jpg...
|
PhpStorm
|
faVsco.js – laravel.log
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
JY-20725-handle-HS-search Project: faVsco.js, menu
JY-20725-handle-HS-search-rate-limit, menu
Start Listening for PHP Debug Connections
HandleHubspotRateLimitTest
Run 'HandleHubspotRateLimitTest'
Debug 'HandleHubspotRateLimitTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
5
133
11
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Console\Commands;
use Carbon\Carbon;
use Carbon\CarbonImmutable;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Redis;
use InvalidArgumentException;
use Jiminny\Jobs\AutomatedReports\RequestGenerateAskJiminnyReportJob;
use Jiminny\Jobs\AutomatedReports\SendReportMailJob;
use Jiminny\Jobs\Crm\Delete\VerifyActivityCrmTaskJob;
use Jiminny\Jobs\Crm\MatchActivityCrmData;
use Jiminny\Jobs\JobDispatcherInterface;
use Jiminny\Models\Activity;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\AutomatedReportResult;
use Jiminny\Models\Team;
use Jiminny\Models\User;
use Jiminny\Repositories\AutomatedReportsRepository;
use Jiminny\Services\Activity\CrmOwnerResolver;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
/**
* Class JiminnyDebugCommand
*
* @package Jiminny\Console\Commands
*/
class JiminnyDebugCommand extends Command
{
public const string FREQUENCY_DAILY = 'daily';
public const string FREQUENCY_WEEKLY = 'weekly';
public const string FREQUENCY_MONTHLY = 'monthly';
public const string FREQUENCY_QUARTERLY = 'quarterly';
public const string FREQUENCY_ONE_OFF = 'one_off';
protected $signature = 'jiminny:debug';
public function handle(
JobDispatcherInterface $jobDispatcher,
AutomatedReportsService $automatedReportsService,
AutomatedReportsRepository $automatedReportsRepository,
UserPilotClient $userPilotClient
): void {
// Choose ONE of the following to run, then comment out the others.
// 1) Dispatch a storm of MatchActivityCrmData jobs against team 2
$this->simulateMatchActivityStorm(teamId: 2, count: 100);
// 2) Dispatch a storm of VerifyActivityCrmTaskJob jobs (simulates DeleteCrmEntityTrait fan-out)
// $this->simulateVerifyTaskStorm(teamId: 2, count: 100);
// 3) Inspect Redis circuit-breaker state for the team's HubSpot portal
// $this->observeRateLimitCache(teamId: 2);
// 4) Make 3 synchronous matchByName calls (foreground, hits API directly)
// $this->rateLimit();
exit(1);
$report = AutomatedReport::find(71);
$last = AutomatedReportResult::query()
->where('report_id', $report->getId())
->whereIn('status', [AutomatedReportResult::STATUS_DEFAULT, AutomatedReportResult::STATUS_FAILED])
// ->where('reason', '!=', AutomatedReportResult::REASON_NOT_ENOUGH_ACTIVITIES)
->whereDate('created_at', CarbonImmutable::now()->toDateString())
->latest()
->first();
$this->info("Last: {$last->getId()}");
exit(1);
$user = User::find(143);
// $count = $automatedReportsRepository->countUserReports($user);
// $this->info("Count: {$count}");
// $count = $automatedReportsRepository->countAllUserReports($user);
// $this->info("All count: {$count}");
$payload = [
'report_type' => 'ask_jiminny',
'frequency' => 'weekly',
];
$userPilotClient->track($user, 'ask-jiminny-report-generated', $payload);
exit(1);
$now = Carbon::now()->subDay(1);
$this->info("Now: {$now->toDateTimeString()}");
$weekStart = Carbon::getWeekStartsAt();
$this->info("Now: {$weekStart}");
// $from = $now->copy()->previousWeekday()->startOfDay();
// $to = $now->copy()->previousWeekday()->endOfDay();
// $fromOld = $now->copy()->subWeeks(1)->startOfDay();
// $toOld = $now->copy()->subDay()->endOfDay();
// $fromNew = $now->copy()->subWeek()->startOfWeek();
// $toNew = $now->copy()->subWeek()->endOfWeek();
// $fromOld = $now->copy()->subMonths(1)->startOfDay();
// $toOld = $now->copy()->subDay()->endOfDay();
// $fromNew = $now->copy()->subMonthNoOverflow()->startOfMonth();
// $toNew = $now->copy()->subMonthNoOverflow()->endOfMonth();
$fromOld = $now->copy()->subMonths(3)->startOfDay();
$toOld = $now->copy()->subDay()->endOfDay();
$fromNew = $now->copy()->subQuarterNoOverflow()->startOfQuarter();
$toNew = $now->copy()->subQuarterNoOverflow()->endOfQuarter();
$this->info("From old: {$fromOld->toDateTimeString()}");
$this->info("To old: {$toOld->toDateTimeString()}");
$this->info("From new: {$fromNew->toDateTimeString()}");
$this->info("To new: {$toNew->toDateTimeString()}");
exit(1);
$report = AutomatedReport::find(71);
$job = new RequestGenerateAskJiminnyReportJob($report->getUuid());
$jobDispatcher->dispatch($job);
exit(1);
// $this->formatDate($jobDispatcher);
// $this->sendMail($jobDispatcher, $automatedReportsService);
// $this->crmService();
$this->getPayload($automatedReportsService);
exit(1);
}
private function crmService()
{
$activity = Activity::find(418141);
$team = Team::find(19);
$config = $team->getCrmConfiguration();
$crmResolver = app(CrmOwnerResolver::class, [
'team' => $team,
'integrationAdmin' => $team->getOwner(),
'providerSlug' => $config->getProviderName(),
]);
$crmService = $crmResolver->prepareCrmService();
$crmService->createTranscriptNotes($activity);
}
private function sendMail(JobDispatcherInterface $jobDispatcher, AutomatedReportsService $automatedReportsService)
{
$reportUuid = '';
// $report = $automatedReportsService->getReportResult($reportUuid);
$report = AutomatedReportResult::find(275);
$validRecipients = $automatedReportsService->getValidRecipientUsers(
$report->getReport(),
includeJiminny: true,
);
$recipient = $validRecipients[0];
$fileName = $automatedReportsService->getReportFileName($report);
$typeName = $report->getReport()->getCustomName()
?? $automatedReportsService->getReportTypeName($report);
$teamsName = $automatedReportsService->getReportTeamsName($report);
$periodName = $automatedReportsService->getReportPeriodName($report);
$s3Path = $automatedReportsService->getMediaPath($report);
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$fileName ' . PHP_EOL . print_r($fileName, true));
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$typeName ' . PHP_EOL . print_r($typeName, true));
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$teamsName ' . PHP_EOL . print_r($teamsName, true));
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$periodName ' . PHP_EOL . print_r($periodName, true));
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$s3Path ' . PHP_EOL . print_r($s3Path, true));
$jobDispatcher->dispatch(
new SendReportMailJob(
reportUuid: $report->getUuid(),
s3Path: $s3Path,
recipientEmail: $recipient['email'],
recipientName: $recipient['name'] ?? null,
fileName: $fileName,
typeName: $typeName,
teamsName: $teamsName,
periodName: $periodName,
isAskJiminny: true,
)
);
exit(1);
}
private function formatDate(JobDispatcherInterface $jobDispatcher): void
{
$customName = 'Custom report name';
// $frequency = self::FREQUENCY_DAILY;
// $frequency = self::FREQUENCY_WEEKLY;
$frequency = self::FREQUENCY_MONTHLY;
// $frequency = self::FREQUENCY_QUARTERLY;
// $frequency = self::FREQUENCY_ONE_OFF;
$period = $this->calculateFromAndToDatePeriod($frequency);
$from = $period['fromDate'];
$to = $period['toDate'];
$periodName = $this->formatReportPeriodName($frequency, $from, $to);
$filenameSuffix = null;
if ($customName) {
if ($filenameSuffix) {
$customName .= " {$filenameSuffix}";
}
$result = $this->sanitizeFileName("{$customName} - {$periodName}");
}
$this->info($result);
}
public function calculateFromAndToDatePeriod(
string $frequency,
?Carbon $fromDate = null,
?Carbon $toDate = null
): array {
if ($frequency === self::FREQUENCY_ONE_OFF) {
return [
'fromDate' => $fromDate,
'toDate' => $toDate,
];
}
$now = Carbon::now();
return match ($frequency) {
self::FREQUENCY_DAILY => [
'fromDate' => $now->copy()->subDay()->startOfDay(),
'toDate' => $now->copy()->subDay()->endOfDay(),
],
self::FREQUENCY_WEEKLY => [
'fromDate' => $now->copy()->subWeeks(1)->startOfDay(),
'toDate' => $now->copy()->subDay()->endOfDay(),
],
self::FREQUENCY_MONTHLY => [
'fromDate' => $now->copy()->subMonths(1)->startOfDay(),
'toDate' => $now->copy()->subDay()->endOfDay(),
],
self::FREQUENCY_QUARTERLY => [
'fromDate' => $now->copy()->subMonths(3)->startOfDay(),
'toDate' => $now->copy()->subDay()->endOfDay(),
],
default => throw new InvalidArgumentException("Unsupported frequency: {$frequency}"),
};
}
private function formatReportPeriodName(string $frequency, Carbon $from, Carbon $to): string
{
$fromYear = $from->format('Y');
$toYear = $to->format('Y');
$differentYears = $fromYear !== $toYear;
switch ($frequency) {
case self::FREQUENCY_DAILY:
return $from->format('j M Y');
case self::FREQUENCY_QUARTERLY:
// 'Jan-Mar 2025' or 'Nov 2024-Jan 2025' if years differ
$startMonth = $from->format('M');
$endMonth = $to->copy()->subMonth();
$endMonthName = $endMonth->format('M');
$endMonthYear = $endMonth->format('Y');
if ($differentYears) {
return "{$startMonth} {$fromYear} - {$endMonthName} {$endMonthYear}";
}
return "{$startMonth} - {$endMonthName} {$toYear}";
case self::FREQUENCY_MONTHLY:
// 'May 2025' - monthly reports are always within the same year
return $from->format('M Y');
case self::FREQUENCY_WEEKLY:
// '4 - 8 Aug 2025', '27 Oct - 3 Nov 2025', or '28 Dec 2024 - 3 Jan 2025' if years differ
$startDay = $from->format('j');
$endDay = $to->format('j');
$startMonth = $from->format('M');
$endMonth = $to->format('M');
if ($differentYears) {
return "{$startDay} {$startMonth} {$fromYear} - {$endDay} {$endMonth} {$toYear}";
}
if ($startMonth !== $endMonth) {
return "{$startDay} {$startMonth} - {$endDay} {$endMonth} {$toYear}";
}
return "{$startDay} - {$endDay} {$endMonth} {$toYear}";
case self::FREQUENCY_ONE_OFF:
// '2 May-31 May 2025' or '15 Dec 2024-15 Jan 2025' if years differ
$startDay = $from->format('j');
$startMonth = $from->format('M');
$endDay = $to->format('j');
$endMonth = $to->format('M');
// If same month and year, use a format like '2-31 May 2025'
if ($startMonth === $endMonth && ! $differentYears) {
return "{$startDay} - {$endDay} {$startMonth} {$toYear}";
}
// If different years, include both years
if ($differentYears) {
return "{$startDay} {$startMonth} {$fromYear} - {$endDay} {$endMonth} {$toYear}";
}
// Same year but different months
return "{$startDay} {$startMonth} - {$endDay} {$endMonth} {$toYear}";
default:
// Default format for unknown frequencies
return $from->format('j M Y') . ' - ' . $to->format('j M Y');
}
}
public function sanitizeFileName(string $fileName): string
{
return str_replace(['/', '\\'], '-', $fileName);
}
private function getPayload(AutomatedReportsService $automatedReportsService)
{
$reportResult = AutomatedReportResult::find(269);
$automatedReport = $reportResult->getReport();
$activityIds = [1,2,3];
$payload = $automatedReportsService->getAskJiminnyGenerateReportPayload(
automatedReport: $automatedReport,
reportResult: $reportResult,
activityIds: $activityIds,
);
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$payload ' . PHP_EOL . print_r($payload, true));
}
private function rateLimit()
{
$team = Team::find(2);
$config = $team->getCrmConfiguration();
$crmResolver = app(CrmOwnerResolver::class, [
'team' => $team,
'integrationAdmin' => $team->getOwner(),
'providerSlug' => $config->getProviderName(),
]);
$crmService = $crmResolver->prepareCrmService();
for ($i = 0 ; $i < 3; $i++) {
// if ($i % 25 === 0) {
// $this->info("Syncing opportunity {$i}");
$this->info("Matching contact {$i}");
// }
// $crmService->syncOpportunity('374720564');
$crmService->matchByName('Robot');
}
}
private function simulateMatchActivityStorm(int $teamId = 2, int $count = 100): void
{
$team = Team::find($teamId);
$config = $team->getCrmConfiguration();
$activities = Activity::query()
->where('crm_configuration_id', $config->getId())
->orderByDesc('id')
->limit($count)
->get();
$this->info("Dispatching {$activities->count()} MatchActivityCrmData jobs (portal={$config->getId()})");
foreach ($activities as $activity) {
MatchActivityCrmData::dispatch($activity->getId(), $config, true);
}
$this->info('Done. Watch logs and run jiminny:debug observeRateLimit to inspect cache state.');
}
private function simulateVerifyTaskStorm(int $teamId = 2, int $count = 100): void
{
$activities = Activity::query()
->where('team_id', $teamId)
->whereNotNull('crm_provider_id')
->orderByDesc('id')
->limit($count)
->get();
$this->info("Dispatching {$activities->count()} VerifyActivityCrmTaskJob jobs");
foreach ($activities as $activity) {
VerifyActivityCrmTaskJob::dispatch($activity->getId());
}
$this->info('Done.');
}
private function observeRateLimitCache(int $teamId = 2): void
{
$team = Team::find($teamId);
$config = $team->getCrmConfiguration();
$key = sprintf('hubspot:ratelimit:portal:%d', $config->getId());
$value = Redis::get($key);
$ttl = Redis::ttl($key);
$this->info("Redis key: {$key}");
$this->info('Value: ' . ($value ?? '(empty)'));
$this->info("TTL: {$ttl}s");
}
}
Show Replace Field
Search History
429
New Line
Match Case
Words
Regex
Replace History
Replace
New Line
Preserve case
1/2
Previous Occurrence
Next Occurrence
Filter Search Results
Open in Window, Multiple Cursors
Click to highlight
Close
Sync Changes
Hide This Notification
Code changed:
Hide
174
Previous Highlighted Error
Next Highlighted Error
[2026-05-11 11:06:08] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {"activity_id":612562,"team_id":2,"email":"[EMAIL]"} {"correlation_id":"c1949c5e-c0f4-431e-ba26-59d49408d871","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:08] local.INFO: [CrmActivityService] CRM matching completed {"activity_id":612562,"participants_processed":2,"exact_matches":0,"domain_matches":1,"best_match_found":true} {"correlation_id":"c1949c5e-c0f4-431e-ba26-59d49408d871","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:08] local.INFO: [ EsUpdateTarget ] Update single target {"target":"activities","purpose":"searchable-observer-update","entityId":612562} {"correlation_id":"c1949c5e-c0f4-431e-ba26-59d49408d871","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:08] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {"entityType":"activities","entityId":612562,"collectionKey":"activities-for-update-priority","withPriority":true} {"correlation_id":"c1949c5e-c0f4-431e-ba26-59d49408d871","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:08] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {"activity":612562,"remote_search":true,"lead_id":null,"contact_id":null,"account_id":26,"opportunity_id":22,"stage_id":89} {"correlation_id":"c1949c5e-c0f4-431e-ba26-59d49408d871","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:09] local.INFO: [MatchActivityCrmData] Starting CRM data matching {"activity":612561,"remote_search":true,"set_configuration":2,"old_state":{"lead_id":null,"contact_id":null,"account_id":26,"opportunity_id":22,"stage_id":89}} {"correlation_id":"b715f531-04da-410f-be31-055aa1c13754","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:09] local.INFO: [ EsUpdateTarget ] Update single target {"target":"activities","purpose":"searchable-observer-update","entityId":612561} {"correlation_id":"b715f531-04da-410f-be31-055aa1c13754","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:09] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {"entityType":"activities","entityId":612561,"collectionKey":"activities-for-update-priority","withPriority":true} {"correlation_id":"b715f531-04da-410f-be31-055aa1c13754","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:09] local.INFO: [MatchActivityCrmData] Participants old state {"activity":612561,"participants":[{"id":999780,"user_id":206,"contact_id":null,"lead_id":null},{"id":999781,"user_id":null,"contact_id":null,"lead_id":null}]} {"correlation_id":"b715f531-04da-410f-be31-055aa1c13754","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:09] local.INFO: [SocialAccountService] Fetching token {"socialAccountId":1499,"provider":"hubspot"} {"correlation_id":"b715f531-04da-410f-be31-055aa1c13754","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:09] local.INFO: [SocialAccountService] Token retrieved {"socialAccountId":1499,"provider":"hubspot"} {"correlation_id":"b715f531-04da-410f-be31-055aa1c13754","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:09] local.INFO: [EncryptedTokenManager] Generating access token. {"mode":"legacy"} {"correlation_id":"b715f531-04da-410f-be31-055aa1c13754","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:09] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {"crm_provider":"hubspot","crm_owner":148,"team_id":2} {"correlation_id":"b715f531-04da-410f-be31-055aa1c13754","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:09] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {"activity_id":612561,"team_id":2,"email":"[EMAIL]"} {"correlation_id":"b715f531-04da-410f-be31-055aa1c13754","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:09] local.INFO: [Prospect match] Cache miss, calling the API {"identifier_type":"email","identifier":"[EMAIL]"} {"correlation_id":"b715f531-04da-410f-be31-055aa1c13754","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:10] local.INFO: [Hubspot] Failed to fetch contact {"email":"[EMAIL]","reason":"[404] Client error: `GET https://api.hubapi.com/crm/v3/objects/contacts/447700174614.447782589921.OeREojLVnk%40txt.staging.jiminny.com?properties=email%2Cfirstname%2Clastname%2Ccountry%2Cphone%2Cmobilephone%2Cjobtitle%2Chubspot_owner_id%2Cassociatedcompanyid%2Cphoto&archived=0&idProperty=email` resulted in a `404 Not Found` response"} {"correlation_id":"b715f531-04da-410f-be31-055aa1c13754","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:10] local.INFO: [Prospect match] API returned empty result, caching the miss with empty prospect data {"identifier_type":"email","identifier":"[EMAIL]"} {"correlation_id":"b715f531-04da-410f-be31-055aa1c13754","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:10] local.INFO: [Prospect match] Resolved company domain from email {"email":"[EMAIL]","domain":"jiminny.com"} {"correlation_id":"b715f531-04da-410f-be31-055aa1c13754","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:10] local.INFO: [CrmActivityService] CRM matching completed {"activity_id":612561,"participants_processed":2,"exact_matches":0,"domain_matches":1,"best_match_found":true} {"correlation_id":"b715f531-04da-410f-be31-055aa1c13754","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:10] local.INFO: [ EsUpdateTarget ] Update single target {"target":"activities","purpose":"searchable-observer-update","entityId":612561} {"correlation_id":"b715f531-04da-410f-be31-055aa1c13754","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:10] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {"entityType":"activities","entityId":612561,"collectionKey":"activities-for-update-priority","withPriority":true} {"correlation_id":"b715f531-04da-410f-be31-055aa1c13754","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:10] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {"activity":612561,"remote_search":true,"lead_id":null,"contact_id":null,"account_id":26,"opportunity_id":22,"stage_id":89} {"correlation_id":"b715f531-04da-410f-be31-055aa1c13754","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:10] local.INFO: [MatchActivityCrmData] Starting CRM data matching {"activity":612560,"remote_search":true,"set_configuration":2,"old_state":{"lead_id":null,"contact_id":null,"account_id":26,"opportunity_id":22,"stage_id":89}} {"correlation_id":"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:10] local.INFO: [ EsUpdateTarget ] Update single target {"target":"activities","purpose":"searchable-observer-update","entityId":612560} {"correlation_id":"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:10] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {"entityType":"activities","entityId":612560,"collectionKey":"activities-for-update-priority","withPriority":true} {"correlation_id":"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:10] local.INFO: [MatchActivityCrmData] Participants old state {"activity":612560,"participants":[{"id":999778,"user_id":null,"contact_id":null,"lead_id":null},{"id":999779,"user_id":206,"contact_id":null,"lead_id":null}]} {"correlation_id":"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [SocialAccountService] Fetching token {"socialAccountId":1499,"provider":"hubspot"} {"correlation_id":"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [SocialAccountService] Token retrieved {"socialAccountId":1499,"provider":"hubspot"} {"correlation_id":"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [EncryptedTokenManager] Generating access token. {"mode":"legacy"} {"correlation_id":"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {"crm_provider":"hubspot","crm_owner":148,"team_id":2} {"correlation_id":"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [Prospect match] Cache / local search hit {"identifier_type":"email","identifier":"[EMAIL]"} {"correlation_id":"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [Prospect match] cached empty result - no API calls, try next matching method {"identifier_type":"email","identifier":"[EMAIL]"} {"correlation_id":"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [Prospect match] Resolved company domain from email {"email":"[EMAIL]","domain":"jiminny.com"} {"correlation_id":"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {"activity_id":612560,"team_id":2,"email":"[EMAIL]"} {"correlation_id":"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [CrmActivityService] CRM matching completed {"activity_id":612560,"participants_processed":2,"exact_matches":0,"domain_matches":1,"best_match_found":true} {"correlation_id":"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [ EsUpdateTarget ] Update single target {"target":"activities","purpose":"searchable-observer-update","entityId":612560} {"correlation_id":"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [ EsUpdateProcessManager ] Finished updating entities in ES {"worker":"","peak_memory":"99.73 MB","elapsed_seconds":0.74,"update_target":"activities","should_iterate_again":false} {"correlation_id":"38935ae1-fcb3-437e-9c9e-1b7415a0b7e1","trace_id":"325a2f9b-9c66-4223-ac02-1a43723f4315"}
[2026-05-11 11:06:11] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {"entityType":"activities","entityId":612560,"collectionKey":"activities-for-update-priority","withPriority":true} {"correlation_id":"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {"activity":612560,"remote_search":true,"lead_id":null,"contact_id":null,"account_id":26,"opportunity_id":22,"stage_id":89} {"correlation_id":"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [MatchActivityCrmData] Starting CRM data matching {"activity":612559,"remote_search":true,"set_configuration":2,"old_state":{"lead_id":null,"contact_id":97,"account_id":69,"opportunity_id":5011,"stage_id":34}} {"correlation_id":"4d59a59c-38a5-4a1d-9379-6ceaf67b747e","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [ EsUpdateTarget ] Update single target {"target":"activities","purpose":"searchable-observer-update","entityId":612559} {"correlation_id":"4d59a59c-38a5-4a1d-9379-6ceaf67b747e","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {"entityType":"activities","entityId":612559,"collectionKey":"activities-for-update-priority","withPriority":true} {"correlation_id":"4d59a59c-38a5-4a1d-9379-6ceaf67b747e","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [MatchActivityCrmData] Participants old state {"activity":612559,"participants":[{"id":999776,"user_id":206,"contact_id":null,"lead_id":null},{"id":999777,"user_id":null,"contact_id":97,"lead_id":null}]} {"correlation_id":"4d59a59c-38a5-4a1d-9379-6ceaf67b747e","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [SocialAccountService] Fetching token {"socialAccountId":1499,"provider":"hubspot"} {"correlation_id":"4d59a59c-38a5-4a1d-9379-6ceaf67b747e","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [SocialAccountService] Token retrieved {"socialAccountId":1499,"provider":"hubspot"} {"correlation_id":"4d59a59c-38a5-4a1d-9379-6ceaf67b747e","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [EncryptedTokenManager] Generating access token. {"mode":"legacy"} {"correlation_id":"4d59a59c-38a5-4a1d-9379-6ceaf67b747e","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {"crm_provider":"hubspot","crm_owner":148,"team_id":2} {"correlation_id":"4d59a59c-38a5-4a1d-9379-6ceaf67b747e","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {"activity_id":612559,"team_id":2,"email":"[EMAIL]"} {"correlation_id":"4d59a59c-38a5-4a1d-9379-6ceaf67b747e","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: ProspectCache - Searching DB for opportunity by owner {"account_id":69,"contact_id":97,"owner_id":206} {"correlation_id":"4d59a59c-38a5-4a1d-9379-6ceaf67b747e","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: ProspectCache - Fallback DB opportunity search {"account_id":69,"contact_id":97} {"correlation_id":"4d59a59c-38a5-4a1d-9379-6ceaf67b747e","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: ProspectCache - Opportunity DB search results {"account_id":69,"contact_id":97,"opportunity_id":5011} {"correlation_id":"4d59a59c-38a5-4a1d-9379-6ceaf67b747e","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [Prospect match] Cache / local search hit {"identifier_type":"email","identifier":"[EMAIL]"} {"correlation_id":"4d59a59c-38a5-4a1d-9379-6ceaf67b747e","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [CrmActivityService] CRM matching completed {"activity_id":612559,"participants_processed":2,"exact_matches":1,"domain_matches":0,"best_match_found":true} {"correlation_id":"4d59a59c-38a5-4a1d-9379-6ceaf67b747e","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [ EsUpdateTarget ] Update single target {"target":"activities","purpose":"searchable-observer-update","entityId":612559} {"correlation_id":"4d59a59c-38a5-4a1d-9379-6ceaf67b747e","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {"entityType":"activities","entityId":612559,"collectionKey":"activities-for-update-priority","withPriority":true} {"correlation_id":"4d59a59c-38a5-4a1d-9379-6ceaf67b747e","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {"activity":612559,"remote_search":true,"lead_id":null,"contact_id":97,"account_id":69,"opportunity_id":5011,"stage_id":34} {"correlation_id":"4d59a59c-38a5-4a1d-9379-6ceaf67b747e","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:12] local.INFO: [MatchActivityCrmData] Starting CRM data matching {"activity":612558,"remote_search":true,"set_configuration":2,"old_state":{"lead_id":null,"contact_id":97,"account_id":69,"opportunity_id":5011,"stage_id":34}} {"correlation_id":"a105800d-d2f7-4772-a534-7791e262461c","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:12] local.INFO: [ EsUpdateTarget ] Update single target {"target":"activities","purpose":"searchable-observer-update","entityId":612558} {"correlation_id":"a105800d-d2f7-4772-a534-7791e262461c","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:12] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {"entityType":"activities","entityId":612558,"collectionKey":"activities-for-update-priority","withPriority":true} {"correlation_id":"a105800d-d2f7-4772-a534-7791e262461c","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:12] local.INFO: [MatchActivityCrmData] Participants old state {"activity":612558,"participants":[{"id":999774,"user_id":206,"contact_id":null,"lead_id":null},{"id":999775,"user_id":null,"contact_id":97,"lead_id":null}]} {"correlation_id":"a105800d-d2f7-4772-a534-7791e262461c","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:12] local.INFO: [SocialAccountService] Fetching token {"socialAccountId":1499,"provider":"hubspot"} {"correlation_id":"a105800d-d2f7-4772-a534-7791e262461c","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:12] local.INFO: [SocialAccountService] Token retrieved {"socialAccountId":1499,"provider":"hubspot"} {"correlation_id":"a105800d-d2f7-4772-a534-7791e262461c","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:12] local.INFO: [EncryptedTokenManager] Generating access token. {"mode":"legacy"} {"correlation_id":"a105800d-d2f7-4772-a534-7791e262461c","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:12] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {"crm_provider":"hubspot","crm_owner":148,"team_id":2} {"correlation_id":"a105800d-d2f7-4772-a534-7791e262461c","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:12] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {"activity_id":612558,"team_id":2,"email":"[EMAIL]"} {"correlation_id":"a105800d-d2f7-4772-a534-7791e262461c","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:12] local.INFO: [Prospect match] Cache / local search hit {"identifier_type":"email","identifier":"[EMAIL]"} {"correlation_id":"a105800d-d2f7-4772-a534-7791e262461c","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:12] local.INFO: [CrmActivityService] CRM matching completed {"activity_id":612558,"participants_processed":2,"exact_matches":1,"domain_matches":0,"best_match_found":true} {"correlation_id":"a105800d-d2f7-4772-a534-7791e262461c","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:12] local.INFO: [ EsUpdateTarget ] Update single target {"target":"activities","purpose":"searchable-observer-update","entityId":612558} {"correlation_id":"a105800d-d2f7-4772-a534-7791e262461c","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:12] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {"entityType":"activities","entityId":612558,"collectionKey":"activities-for-update-priority","withPriority":true} {"correlation_id":"a105800d-d2f7-4772-a534-7791e262461c","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:12] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {"activity":612558,"remote_search":true,"lead_id":null,"contact_id":97,"account_id":69,"opportunity_id":5011,"stage_id":34} {"correlation_id":"a105800d-d2f7-4772-a534-7791e262461c","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:13] local.INFO: [MatchActivityCrmData] Starting CRM data matching {"activity":612557,"remote_search":true,"set_configuration":2,"old_state":{"lead_id":null,"contact_id":97,"account_id":69,"opportunity_id":5011,"stage_id":34}} {"correlation_id":"3ec57437-5796-4e44-8bf0-aaec149ac813","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:13] local.INFO: [ EsUpdateTarget ] Update single target {"target":"activities","purpose":"searchable-observer-update","entityId":612557} {"correlation_id":"3ec57437-5796-4e44-8bf0-aaec149ac813","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:13] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {"entityType":"activities","entityId":612557,"collectionKey":"activities-for-update-priority","withPriority":true} {"correlation_id":"3ec57437-5796-4e44-8bf0-aaec149ac813","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:13] local.INFO: [MatchActivityCrmData] Participants old state {"activity":612557,"participants":[{"id":999772,"user_id":206,"contact_id":null,"lead_id":null},{"id":999773,"user_id":null,"contact_id":97,"lead_id":null}]} {"correlation_id":"3ec57437-5796-4e44-8bf0-aaec149ac813","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:13] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"mailbox:skip-lists:refresh","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.727} {"correlation_id":"5203040f-23be-43e6-8410-ba49ee1b38b4","trace_id":"e787953d-9b78-4b63-a987-4f93f6892984"}
[2026-05-11 11:06:13] local.INFO: [SocialAccountService] Fetching token {"socialAccountId":1499,"provider":"hubspot"} {"correlation_id":"3ec57437-5796-4e44-8bf0-aaec149ac813","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:13] local.INFO: [SocialAccountService] Token retrieved {"socialAccountId":1499,"provider":"hubspot"} {"correlation_id":"3ec57437-5796-4e44-8bf0-aaec149ac813","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:13] local.INFO: [EncryptedTokenManager] Generating access token. {"mode":"legacy"} {"correlation_id":"3ec57437-5796-4e44-8bf0-aaec149ac813","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:13] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {"crm_provider":"hubspot","crm_owner":148,"team_id":2} {"correlation_id":"3ec57437-5796-4e44-8bf0-aaec149ac813","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:13] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {"activity_id":612557,"team_id":2,"email":"[EMAIL]"} {"correlation_id":"3ec57437-5796-4e44-8bf0-aaec149ac813","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:13] local.INFO: [Prospect match] Cache / local search hit {"identifier_type":"email","identifier":"[EMAIL]"} {"correlation_id":"3ec57437-5796-4e44-8bf0-aaec149ac813","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:13] local.INFO: [CrmActivityService] CRM matching completed {"activity_id":612557,"participants_processed":2,"exact_matches":1,"domain_matches":0,"best_match_found":true} {"correlation_id":"3ec57437-5796-4e44-8bf0-aaec149ac813","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:13] local.INFO: [ EsUpdateTarget ] Update single target {"target":"activities","purpose":"searchable-observer-update","entityId":612557} {"correlation_id":"3ec57437-5796-4e44-8bf0-aaec149ac813","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:13] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {"entityType":"activities","entityId":612557,"collectionKey":"activities-for-update-priority","withPriority":true} {"correlation_id":"3ec57437-5796-4e44-8bf0-aaec149ac813","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:13] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {"activity":612557,"remote_search":true,"lead_id":null,"contact_id":97,"account_id":69,"opportunity_id":5011,"stage_id":34} {"correlation_id":"3ec57437-5796-4e44-8bf0-aaec149ac813","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:13] local.INFO: Jiminny\Console\Commands\Command::run Memory usage for command {"command":"mailbox:skip-lists:refresh","memoryBeforeCommandInMb":60.0,"memoryAfterCommandInMB":60.0,"memoryPeakBeforeCommandInMb":99.727,"memoryPeakAfterCommandInMB":99.727} {"correlation_id":"5203040f-23be-43e6-8410-ba49ee1b38b4","trace_id":"e787953d-9b78-4b63-a987-4f93f6892984"}
[2026-05-11 11:06:13] local.INFO: [MatchActivityCrmData] Starting CRM data matching {"activity":612556,"remote_search":true,"set_configuration":2,"old_state":{"lead_id":null,"contact_id":97,"account_id":69,"opportunity_id":5011,"stage_id":34}} {"correlation_id":"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:13] local.INFO: [ EsUpdateTarget ] Update single target {"target":"activities","purpose":"searchable-observer-update","entityId":612556} {"correlation_id":"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:13] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {"entityType":"activities","entityId":612556,"collectionKey":"activities-for-update-priority","withPriority":true} {"correlation_id":"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:13] local.INFO: [MatchActivityCrmData] Participants old state {"activity":612556,"participants":[{"id":999770,"user_id":206,"contact_id":null,"lead_id":null},{"id":999771,"user_id":null,"contact_id":97,"lead_id":null}]} {"correlation_id":"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:13] local.INFO: [SocialAccountService] Fetching token {"socialAccountId":1499,"provider":"hubspot"} {"correlation_id":"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:13] local.INFO: [SocialAccountService] Token retrieved {"socialAccountId":1499,"provider":"hubspot"} {"correlation_id":"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:13] local.INFO: [EncryptedTokenManager] Generating access token. {"mode":"legacy"} {"correlation_id":"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:13] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {"crm_provider":"hubspot","crm_owner":148,"team_id":2} {"correlation_id":"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:14] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {"activity_id":612556,"team_id":2,"email":"[EMAIL]"} {"correlation_id":"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:14] local.INFO: [Prospect match] Cache / local search hit {"identifier_type":"email","identifier":"[EMAIL]"} {"correlation_id":"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:14] local.INFO: [CrmActivityService] CRM matching completed {"activity_id":612556,"participants_processed":2,"exact_matches":1,"domain_matches":0,"best_match_found":true} {"correlation_id":"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:14] local.INFO: [ EsUpdateTarget ] Update single target {"target":"activities","purpose":"searchable-observer-update","entityId":612556} {"correlation_id":"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:14] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {"entityType":"activities","entityId":612556,"collectionKey":"activities-for-update-priority","withPriority":true} {"correlation_id":"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:14] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {"activity":612556,"remote_search":true,"lead_id":null,"contact_id":97,"account_id":69,"opportunity_id":5011,"stage_id":34} {"correlation_id":"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:14] local.INFO: [MatchActivityCrmData] Starting CRM data matching {"activity":612555,"remote_search":true,"set_configuration":2,"old_state":{"lead_id":null,"contact_id":97,"account_id":69,"opportunity_id":5011,"stage_id":34}} {"correlation_id":"3e9d1a70-5491-44ae-8f28-5cb9b60e1859","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:14] local.INFO: [ EsUpdateTarget ] Update single target {"target":"activities","purpose":"searchable-observer-update","entityId":612555} {"correlation_id":"3e9d1a70-5491-44ae-8f28-5cb9b60e1859","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:14] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {"entityType":"activities","entityId":612555,"collectionKey":"activities-for-update-priority","withPriority":true} {"correlation_id":"3e9d1a70-5491-44ae-8f28-5cb9b60e1859","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:14] local.INFO: [MatchActivityCrmData] Participants old state {"activity":612555,"participants":[{"id":999768,"user_id":206,"contact_id":null,"lead_id":null},{"id":999769,"user_id":null,"contact_id":97,"lead_id":null}]} {"correlation_id":"3e9d1a70-5491-44ae-8f28-5cb9b60e1859","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:14] local.INFO: [SocialAccountService] Fetching token {"socialAccountId":1499,"provider":"hubspot"} {"correlation_id":"3e9d1a70-5491-44ae-8f28-5cb9b60e1859","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:14] local.INFO: [SocialAccountService] Token retrieved {"socialAccountId":1499,"provider":"hubspot"} {"correlation_id":"3e9d1a70-5491-44ae-8f28-5cb9b60e1859","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:14] local.INFO: [EncryptedTokenManager] Generating access token. {"mode":"legacy"} {"correlation_id":"3e9d1a70-5491-44ae-8f28-5cb9b60e1859","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:14] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {"crm_provider":"hubspot","crm_owner":148,"team_id":2} {"correlation_id":"3e9d1a70-5491-44ae-8f28-5cb9b60e1859","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:14] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {"activity_id":612555,"team_id":2,"email":"[EMAIL]"} {"correlation_id":"3e9d1a70-5491-44ae-8f28-5cb9b60e1859","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:14] local.INFO: [Prospect match] Cache / local search hit {"identifier_type":"email","identifier":"[EMAIL]"} {"correlation_id":"3e9d1a70-5491-44ae-8f28-5cb9b60e1859","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:14] local.INFO: [CrmActivityService] CRM matching completed {"activity_id":612555,"participants_processed":2,"exact_matches":1,"domain_matches":0,"best_match_found":true} {"correlation_id":"3e9d1a70-5491-44ae-8f28-5cb9b60e1859","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:14] local.INFO: [ EsUpdateTarget ] Update single target {"target":"activities","purpose":"searchable-observer-update","entityId":612555} {"correlation_id":"3e9d1a70-5491-44ae-8f28-5cb9b60e1859","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:14] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {"entityType":"activities","entityId":612555,"collectionKey":"activities-for-update-priority","withPriority":true} {"correlation_id":"3e9d1a70-5491-44ae-8f28-5cb9b60e1859","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:14] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {"activity":612555,"remote_search":true,"lead_id":null,"contact_id":97,"account_id":69,"opportunity_id":5011,"stage_id":34} {"correlation_id":"3e9d1a70-5491-44ae-8f28-5cb9b60e1859","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:14] local.INFO: [MatchActivityCrmData] Starting CRM data matching {"activity":612554,"remote_search":true,"set_configuration":2,"old_state":{"lead_id":null,"contact_id":97,"account_id":69,"opportunity_id":5011,"stage_id":34}} {"correlation_id":"7101e979-3a7e-4b2c-bf04-dc6a24ba1356","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:14] local.INFO: [ EsUpdateTarget ] Update single target {"target":"activities","purpose":"searchable-observer-update","entityId":612554} {"correlation_id":"7101e979-3a7e-4b2c-bf04-dc6a24ba1356","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:14] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {"entityType":"activities","entityId":612554,"collectionKey":"activities-for-update-priority",...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"bounds":{"left":0.025930852,"top":0.019952115,"width":0.03856383,"height":0.025538707},"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JY-20725-handle-HS-search-rate-limit, menu","depth":5,"bounds":{"left":0.064494684,"top":0.019952115,"width":0.09541223,"height":0.025538707},"on_screen":true,"help_text":"Git Branch: JY-20725-handle-HS-search-rate-limit","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"bounds":{"left":0.82413566,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"HandleHubspotRateLimitTest","depth":6,"bounds":{"left":0.8394282,"top":0.019952115,"width":0.076130316,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'HandleHubspotRateLimitTest'","depth":6,"bounds":{"left":0.9155585,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'HandleHubspotRateLimitTest'","depth":6,"bounds":{"left":0.9268617,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"bounds":{"left":0.9381649,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"bounds":{"left":0.96609044,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"bounds":{"left":0.9773936,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"bounds":{"left":0.9886968,"top":0.019952115,"width":0.011303186,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"5","depth":4,"bounds":{"left":0.5043218,"top":0.17478053,"width":0.007978723,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"133","depth":4,"bounds":{"left":0.5142952,"top":0.17478053,"width":0.011968086,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"11","depth":4,"bounds":{"left":0.52825797,"top":0.17478053,"width":0.008976064,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.53889626,"top":0.17318435,"width":0.00731383,"height":0.018355945},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.5462101,"top":0.17318435,"width":0.006981383,"height":0.018355945},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Console\\Commands;\n\nuse Carbon\\Carbon;\nuse Carbon\\CarbonImmutable;\nuse Illuminate\\Console\\Command;\nuse Illuminate\\Support\\Facades\\Redis;\nuse InvalidArgumentException;\nuse Jiminny\\Jobs\\AutomatedReports\\RequestGenerateAskJiminnyReportJob;\nuse Jiminny\\Jobs\\AutomatedReports\\SendReportMailJob;\nuse Jiminny\\Jobs\\Crm\\Delete\\VerifyActivityCrmTaskJob;\nuse Jiminny\\Jobs\\Crm\\MatchActivityCrmData;\nuse Jiminny\\Jobs\\JobDispatcherInterface;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\AutomatedReportResult;\nuse Jiminny\\Models\\Team;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Repositories\\AutomatedReportsRepository;\nuse Jiminny\\Services\\Activity\\CrmOwnerResolver;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\n\n/**\n * Class JiminnyDebugCommand\n *\n * @package Jiminny\\Console\\Commands\n */\nclass JiminnyDebugCommand extends Command\n{\n public const string FREQUENCY_DAILY = 'daily';\n public const string FREQUENCY_WEEKLY = 'weekly';\n public const string FREQUENCY_MONTHLY = 'monthly';\n public const string FREQUENCY_QUARTERLY = 'quarterly';\n public const string FREQUENCY_ONE_OFF = 'one_off';\n protected $signature = 'jiminny:debug';\n\n public function handle(\n JobDispatcherInterface $jobDispatcher,\n AutomatedReportsService $automatedReportsService,\n AutomatedReportsRepository $automatedReportsRepository,\n UserPilotClient $userPilotClient\n ): void {\n // Choose ONE of the following to run, then comment out the others.\n // 1) Dispatch a storm of MatchActivityCrmData jobs against team 2\n $this->simulateMatchActivityStorm(teamId: 2, count: 100);\n\n // 2) Dispatch a storm of VerifyActivityCrmTaskJob jobs (simulates DeleteCrmEntityTrait fan-out)\n // $this->simulateVerifyTaskStorm(teamId: 2, count: 100);\n\n // 3) Inspect Redis circuit-breaker state for the team's HubSpot portal\n // $this->observeRateLimitCache(teamId: 2);\n\n // 4) Make 3 synchronous matchByName calls (foreground, hits API directly)\n // $this->rateLimit();\n exit(1);\n\n\n\n $report = AutomatedReport::find(71);\n $last = AutomatedReportResult::query()\n ->where('report_id', $report->getId())\n ->whereIn('status', [AutomatedReportResult::STATUS_DEFAULT, AutomatedReportResult::STATUS_FAILED])\n// ->where('reason', '!=', AutomatedReportResult::REASON_NOT_ENOUGH_ACTIVITIES)\n ->whereDate('created_at', CarbonImmutable::now()->toDateString())\n ->latest()\n ->first();\n\n $this->info(\"Last: {$last->getId()}\");\n\n exit(1);\n\n $user = User::find(143);\n // $count = $automatedReportsRepository->countUserReports($user);\n // $this->info(\"Count: {$count}\");\n // $count = $automatedReportsRepository->countAllUserReports($user);\n // $this->info(\"All count: {$count}\");\n\n $payload = [\n 'report_type' => 'ask_jiminny',\n 'frequency' => 'weekly',\n ];\n $userPilotClient->track($user, 'ask-jiminny-report-generated', $payload);\n\n exit(1);\n\n $now = Carbon::now()->subDay(1);\n $this->info(\"Now: {$now->toDateTimeString()}\");\n $weekStart = Carbon::getWeekStartsAt();\n $this->info(\"Now: {$weekStart}\");\n\n // $from = $now->copy()->previousWeekday()->startOfDay();\n // $to = $now->copy()->previousWeekday()->endOfDay();\n\n // $fromOld = $now->copy()->subWeeks(1)->startOfDay();\n // $toOld = $now->copy()->subDay()->endOfDay();\n // $fromNew = $now->copy()->subWeek()->startOfWeek();\n // $toNew = $now->copy()->subWeek()->endOfWeek();\n\n // $fromOld = $now->copy()->subMonths(1)->startOfDay();\n // $toOld = $now->copy()->subDay()->endOfDay();\n // $fromNew = $now->copy()->subMonthNoOverflow()->startOfMonth();\n // $toNew = $now->copy()->subMonthNoOverflow()->endOfMonth();\n\n $fromOld = $now->copy()->subMonths(3)->startOfDay();\n $toOld = $now->copy()->subDay()->endOfDay();\n $fromNew = $now->copy()->subQuarterNoOverflow()->startOfQuarter();\n $toNew = $now->copy()->subQuarterNoOverflow()->endOfQuarter();\n\n $this->info(\"From old: {$fromOld->toDateTimeString()}\");\n $this->info(\"To old: {$toOld->toDateTimeString()}\");\n $this->info(\"From new: {$fromNew->toDateTimeString()}\");\n $this->info(\"To new: {$toNew->toDateTimeString()}\");\n\n exit(1);\n\n $report = AutomatedReport::find(71);\n\n $job = new RequestGenerateAskJiminnyReportJob($report->getUuid());\n $jobDispatcher->dispatch($job);\n\n exit(1);\n\n\n // $this->formatDate($jobDispatcher);\n // $this->sendMail($jobDispatcher, $automatedReportsService);\n // $this->crmService();\n\n $this->getPayload($automatedReportsService);\n\n exit(1);\n }\n\n\n\n private function crmService()\n {\n $activity = Activity::find(418141);\n\n $team = Team::find(19);\n $config = $team->getCrmConfiguration();\n\n $crmResolver = app(CrmOwnerResolver::class, [\n 'team' => $team,\n 'integrationAdmin' => $team->getOwner(),\n 'providerSlug' => $config->getProviderName(),\n ]);\n\n $crmService = $crmResolver->prepareCrmService();\n\n $crmService->createTranscriptNotes($activity);\n }\n\n private function sendMail(JobDispatcherInterface $jobDispatcher, AutomatedReportsService $automatedReportsService)\n {\n $reportUuid = '';\n // $report = $automatedReportsService->getReportResult($reportUuid);\n $report = AutomatedReportResult::find(275);\n $validRecipients = $automatedReportsService->getValidRecipientUsers(\n $report->getReport(),\n includeJiminny: true,\n );\n\n $recipient = $validRecipients[0];\n\n $fileName = $automatedReportsService->getReportFileName($report);\n $typeName = $report->getReport()->getCustomName()\n ?? $automatedReportsService->getReportTypeName($report);\n $teamsName = $automatedReportsService->getReportTeamsName($report);\n $periodName = $automatedReportsService->getReportPeriodName($report);\n $s3Path = $automatedReportsService->getMediaPath($report);\n\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$fileName ' . PHP_EOL . print_r($fileName, true));\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$typeName ' . PHP_EOL . print_r($typeName, true));\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$teamsName ' . PHP_EOL . print_r($teamsName, true));\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$periodName ' . PHP_EOL . print_r($periodName, true));\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$s3Path ' . PHP_EOL . print_r($s3Path, true));\n\n $jobDispatcher->dispatch(\n new SendReportMailJob(\n reportUuid: $report->getUuid(),\n s3Path: $s3Path,\n recipientEmail: $recipient['email'],\n recipientName: $recipient['name'] ?? null,\n fileName: $fileName,\n typeName: $typeName,\n teamsName: $teamsName,\n periodName: $periodName,\n isAskJiminny: true,\n )\n );\n\n exit(1);\n }\n\n private function formatDate(JobDispatcherInterface $jobDispatcher): void\n {\n $customName = 'Custom report name';\n // $frequency = self::FREQUENCY_DAILY;\n // $frequency = self::FREQUENCY_WEEKLY;\n $frequency = self::FREQUENCY_MONTHLY;\n // $frequency = self::FREQUENCY_QUARTERLY;\n // $frequency = self::FREQUENCY_ONE_OFF;\n $period = $this->calculateFromAndToDatePeriod($frequency);\n $from = $period['fromDate'];\n $to = $period['toDate'];\n $periodName = $this->formatReportPeriodName($frequency, $from, $to);\n $filenameSuffix = null;\n\n if ($customName) {\n if ($filenameSuffix) {\n $customName .= \" {$filenameSuffix}\";\n }\n\n $result = $this->sanitizeFileName(\"{$customName} - {$periodName}\");\n }\n\n $this->info($result);\n }\n\n public function calculateFromAndToDatePeriod(\n string $frequency,\n ?Carbon $fromDate = null,\n ?Carbon $toDate = null\n ): array {\n if ($frequency === self::FREQUENCY_ONE_OFF) {\n return [\n 'fromDate' => $fromDate,\n 'toDate' => $toDate,\n ];\n }\n\n $now = Carbon::now();\n\n return match ($frequency) {\n self::FREQUENCY_DAILY => [\n 'fromDate' => $now->copy()->subDay()->startOfDay(),\n 'toDate' => $now->copy()->subDay()->endOfDay(),\n ],\n self::FREQUENCY_WEEKLY => [\n 'fromDate' => $now->copy()->subWeeks(1)->startOfDay(),\n 'toDate' => $now->copy()->subDay()->endOfDay(),\n ],\n self::FREQUENCY_MONTHLY => [\n 'fromDate' => $now->copy()->subMonths(1)->startOfDay(),\n 'toDate' => $now->copy()->subDay()->endOfDay(),\n ],\n self::FREQUENCY_QUARTERLY => [\n 'fromDate' => $now->copy()->subMonths(3)->startOfDay(),\n 'toDate' => $now->copy()->subDay()->endOfDay(),\n ],\n default => throw new InvalidArgumentException(\"Unsupported frequency: {$frequency}\"),\n };\n }\n\n private function formatReportPeriodName(string $frequency, Carbon $from, Carbon $to): string\n {\n $fromYear = $from->format('Y');\n $toYear = $to->format('Y');\n $differentYears = $fromYear !== $toYear;\n\n switch ($frequency) {\n case self::FREQUENCY_DAILY:\n return $from->format('j M Y');\n\n case self::FREQUENCY_QUARTERLY:\n // 'Jan-Mar 2025' or 'Nov 2024-Jan 2025' if years differ\n $startMonth = $from->format('M');\n $endMonth = $to->copy()->subMonth();\n $endMonthName = $endMonth->format('M');\n $endMonthYear = $endMonth->format('Y');\n\n if ($differentYears) {\n return \"{$startMonth} {$fromYear} - {$endMonthName} {$endMonthYear}\";\n }\n\n return \"{$startMonth} - {$endMonthName} {$toYear}\";\n\n case self::FREQUENCY_MONTHLY:\n // 'May 2025' - monthly reports are always within the same year\n return $from->format('M Y');\n\n case self::FREQUENCY_WEEKLY:\n // '4 - 8 Aug 2025', '27 Oct - 3 Nov 2025', or '28 Dec 2024 - 3 Jan 2025' if years differ\n $startDay = $from->format('j');\n $endDay = $to->format('j');\n $startMonth = $from->format('M');\n $endMonth = $to->format('M');\n\n if ($differentYears) {\n return \"{$startDay} {$startMonth} {$fromYear} - {$endDay} {$endMonth} {$toYear}\";\n }\n\n if ($startMonth !== $endMonth) {\n return \"{$startDay} {$startMonth} - {$endDay} {$endMonth} {$toYear}\";\n }\n\n return \"{$startDay} - {$endDay} {$endMonth} {$toYear}\";\n\n case self::FREQUENCY_ONE_OFF:\n // '2 May-31 May 2025' or '15 Dec 2024-15 Jan 2025' if years differ\n $startDay = $from->format('j');\n $startMonth = $from->format('M');\n $endDay = $to->format('j');\n $endMonth = $to->format('M');\n\n // If same month and year, use a format like '2-31 May 2025'\n if ($startMonth === $endMonth && ! $differentYears) {\n return \"{$startDay} - {$endDay} {$startMonth} {$toYear}\";\n }\n\n // If different years, include both years\n if ($differentYears) {\n return \"{$startDay} {$startMonth} {$fromYear} - {$endDay} {$endMonth} {$toYear}\";\n }\n\n // Same year but different months\n return \"{$startDay} {$startMonth} - {$endDay} {$endMonth} {$toYear}\";\n\n default:\n // Default format for unknown frequencies\n return $from->format('j M Y') . ' - ' . $to->format('j M Y');\n }\n }\n\n public function sanitizeFileName(string $fileName): string\n {\n return str_replace(['/', '\\\\'], '-', $fileName);\n }\n\n private function getPayload(AutomatedReportsService $automatedReportsService)\n {\n $reportResult = AutomatedReportResult::find(269);\n $automatedReport = $reportResult->getReport();\n $activityIds = [1,2,3];\n $payload = $automatedReportsService->getAskJiminnyGenerateReportPayload(\n automatedReport: $automatedReport,\n reportResult: $reportResult,\n activityIds: $activityIds,\n );\n\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$payload ' . PHP_EOL . print_r($payload, true));\n }\n\n private function rateLimit()\n {\n $team = Team::find(2);\n $config = $team->getCrmConfiguration();\n\n $crmResolver = app(CrmOwnerResolver::class, [\n 'team' => $team,\n 'integrationAdmin' => $team->getOwner(),\n 'providerSlug' => $config->getProviderName(),\n ]);\n\n $crmService = $crmResolver->prepareCrmService();\n\n for ($i = 0 ; $i < 3; $i++) {\n// if ($i % 25 === 0) {\n// $this->info(\"Syncing opportunity {$i}\");\n $this->info(\"Matching contact {$i}\");\n// }\n// $crmService->syncOpportunity('374720564');\n $crmService->matchByName('Robot');\n }\n }\n\n private function simulateMatchActivityStorm(int $teamId = 2, int $count = 100): void\n {\n $team = Team::find($teamId);\n $config = $team->getCrmConfiguration();\n\n $activities = Activity::query()\n ->where('crm_configuration_id', $config->getId())\n ->orderByDesc('id')\n ->limit($count)\n ->get();\n\n $this->info(\"Dispatching {$activities->count()} MatchActivityCrmData jobs (portal={$config->getId()})\");\n\n foreach ($activities as $activity) {\n MatchActivityCrmData::dispatch($activity->getId(), $config, true);\n }\n\n $this->info('Done. Watch logs and run jiminny:debug observeRateLimit to inspect cache state.');\n }\n\n private function simulateVerifyTaskStorm(int $teamId = 2, int $count = 100): void\n {\n $activities = Activity::query()\n ->where('team_id', $teamId)\n ->whereNotNull('crm_provider_id')\n ->orderByDesc('id')\n ->limit($count)\n ->get();\n\n $this->info(\"Dispatching {$activities->count()} VerifyActivityCrmTaskJob jobs\");\n\n foreach ($activities as $activity) {\n VerifyActivityCrmTaskJob::dispatch($activity->getId());\n }\n\n $this->info('Done.');\n }\n\n private function observeRateLimitCache(int $teamId = 2): void\n {\n $team = Team::find($teamId);\n $config = $team->getCrmConfiguration();\n $key = sprintf('hubspot:ratelimit:portal:%d', $config->getId());\n\n $value = Redis::get($key);\n $ttl = Redis::ttl($key);\n\n $this->info(\"Redis key: {$key}\");\n $this->info('Value: ' . ($value ?? '(empty)'));\n $this->info(\"TTL: {$ttl}s\");\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Console\\Commands;\n\nuse Carbon\\Carbon;\nuse Carbon\\CarbonImmutable;\nuse Illuminate\\Console\\Command;\nuse Illuminate\\Support\\Facades\\Redis;\nuse InvalidArgumentException;\nuse Jiminny\\Jobs\\AutomatedReports\\RequestGenerateAskJiminnyReportJob;\nuse Jiminny\\Jobs\\AutomatedReports\\SendReportMailJob;\nuse Jiminny\\Jobs\\Crm\\Delete\\VerifyActivityCrmTaskJob;\nuse Jiminny\\Jobs\\Crm\\MatchActivityCrmData;\nuse Jiminny\\Jobs\\JobDispatcherInterface;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\AutomatedReportResult;\nuse Jiminny\\Models\\Team;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Repositories\\AutomatedReportsRepository;\nuse Jiminny\\Services\\Activity\\CrmOwnerResolver;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\n\n/**\n * Class JiminnyDebugCommand\n *\n * @package Jiminny\\Console\\Commands\n */\nclass JiminnyDebugCommand extends Command\n{\n public const string FREQUENCY_DAILY = 'daily';\n public const string FREQUENCY_WEEKLY = 'weekly';\n public const string FREQUENCY_MONTHLY = 'monthly';\n public const string FREQUENCY_QUARTERLY = 'quarterly';\n public const string FREQUENCY_ONE_OFF = 'one_off';\n protected $signature = 'jiminny:debug';\n\n public function handle(\n JobDispatcherInterface $jobDispatcher,\n AutomatedReportsService $automatedReportsService,\n AutomatedReportsRepository $automatedReportsRepository,\n UserPilotClient $userPilotClient\n ): void {\n // Choose ONE of the following to run, then comment out the others.\n // 1) Dispatch a storm of MatchActivityCrmData jobs against team 2\n $this->simulateMatchActivityStorm(teamId: 2, count: 100);\n\n // 2) Dispatch a storm of VerifyActivityCrmTaskJob jobs (simulates DeleteCrmEntityTrait fan-out)\n // $this->simulateVerifyTaskStorm(teamId: 2, count: 100);\n\n // 3) Inspect Redis circuit-breaker state for the team's HubSpot portal\n // $this->observeRateLimitCache(teamId: 2);\n\n // 4) Make 3 synchronous matchByName calls (foreground, hits API directly)\n // $this->rateLimit();\n exit(1);\n\n\n\n $report = AutomatedReport::find(71);\n $last = AutomatedReportResult::query()\n ->where('report_id', $report->getId())\n ->whereIn('status', [AutomatedReportResult::STATUS_DEFAULT, AutomatedReportResult::STATUS_FAILED])\n// ->where('reason', '!=', AutomatedReportResult::REASON_NOT_ENOUGH_ACTIVITIES)\n ->whereDate('created_at', CarbonImmutable::now()->toDateString())\n ->latest()\n ->first();\n\n $this->info(\"Last: {$last->getId()}\");\n\n exit(1);\n\n $user = User::find(143);\n // $count = $automatedReportsRepository->countUserReports($user);\n // $this->info(\"Count: {$count}\");\n // $count = $automatedReportsRepository->countAllUserReports($user);\n // $this->info(\"All count: {$count}\");\n\n $payload = [\n 'report_type' => 'ask_jiminny',\n 'frequency' => 'weekly',\n ];\n $userPilotClient->track($user, 'ask-jiminny-report-generated', $payload);\n\n exit(1);\n\n $now = Carbon::now()->subDay(1);\n $this->info(\"Now: {$now->toDateTimeString()}\");\n $weekStart = Carbon::getWeekStartsAt();\n $this->info(\"Now: {$weekStart}\");\n\n // $from = $now->copy()->previousWeekday()->startOfDay();\n // $to = $now->copy()->previousWeekday()->endOfDay();\n\n // $fromOld = $now->copy()->subWeeks(1)->startOfDay();\n // $toOld = $now->copy()->subDay()->endOfDay();\n // $fromNew = $now->copy()->subWeek()->startOfWeek();\n // $toNew = $now->copy()->subWeek()->endOfWeek();\n\n // $fromOld = $now->copy()->subMonths(1)->startOfDay();\n // $toOld = $now->copy()->subDay()->endOfDay();\n // $fromNew = $now->copy()->subMonthNoOverflow()->startOfMonth();\n // $toNew = $now->copy()->subMonthNoOverflow()->endOfMonth();\n\n $fromOld = $now->copy()->subMonths(3)->startOfDay();\n $toOld = $now->copy()->subDay()->endOfDay();\n $fromNew = $now->copy()->subQuarterNoOverflow()->startOfQuarter();\n $toNew = $now->copy()->subQuarterNoOverflow()->endOfQuarter();\n\n $this->info(\"From old: {$fromOld->toDateTimeString()}\");\n $this->info(\"To old: {$toOld->toDateTimeString()}\");\n $this->info(\"From new: {$fromNew->toDateTimeString()}\");\n $this->info(\"To new: {$toNew->toDateTimeString()}\");\n\n exit(1);\n\n $report = AutomatedReport::find(71);\n\n $job = new RequestGenerateAskJiminnyReportJob($report->getUuid());\n $jobDispatcher->dispatch($job);\n\n exit(1);\n\n\n // $this->formatDate($jobDispatcher);\n // $this->sendMail($jobDispatcher, $automatedReportsService);\n // $this->crmService();\n\n $this->getPayload($automatedReportsService);\n\n exit(1);\n }\n\n\n\n private function crmService()\n {\n $activity = Activity::find(418141);\n\n $team = Team::find(19);\n $config = $team->getCrmConfiguration();\n\n $crmResolver = app(CrmOwnerResolver::class, [\n 'team' => $team,\n 'integrationAdmin' => $team->getOwner(),\n 'providerSlug' => $config->getProviderName(),\n ]);\n\n $crmService = $crmResolver->prepareCrmService();\n\n $crmService->createTranscriptNotes($activity);\n }\n\n private function sendMail(JobDispatcherInterface $jobDispatcher, AutomatedReportsService $automatedReportsService)\n {\n $reportUuid = '';\n // $report = $automatedReportsService->getReportResult($reportUuid);\n $report = AutomatedReportResult::find(275);\n $validRecipients = $automatedReportsService->getValidRecipientUsers(\n $report->getReport(),\n includeJiminny: true,\n );\n\n $recipient = $validRecipients[0];\n\n $fileName = $automatedReportsService->getReportFileName($report);\n $typeName = $report->getReport()->getCustomName()\n ?? $automatedReportsService->getReportTypeName($report);\n $teamsName = $automatedReportsService->getReportTeamsName($report);\n $periodName = $automatedReportsService->getReportPeriodName($report);\n $s3Path = $automatedReportsService->getMediaPath($report);\n\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$fileName ' . PHP_EOL . print_r($fileName, true));\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$typeName ' . PHP_EOL . print_r($typeName, true));\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$teamsName ' . PHP_EOL . print_r($teamsName, true));\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$periodName ' . PHP_EOL . print_r($periodName, true));\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$s3Path ' . PHP_EOL . print_r($s3Path, true));\n\n $jobDispatcher->dispatch(\n new SendReportMailJob(\n reportUuid: $report->getUuid(),\n s3Path: $s3Path,\n recipientEmail: $recipient['email'],\n recipientName: $recipient['name'] ?? null,\n fileName: $fileName,\n typeName: $typeName,\n teamsName: $teamsName,\n periodName: $periodName,\n isAskJiminny: true,\n )\n );\n\n exit(1);\n }\n\n private function formatDate(JobDispatcherInterface $jobDispatcher): void\n {\n $customName = 'Custom report name';\n // $frequency = self::FREQUENCY_DAILY;\n // $frequency = self::FREQUENCY_WEEKLY;\n $frequency = self::FREQUENCY_MONTHLY;\n // $frequency = self::FREQUENCY_QUARTERLY;\n // $frequency = self::FREQUENCY_ONE_OFF;\n $period = $this->calculateFromAndToDatePeriod($frequency);\n $from = $period['fromDate'];\n $to = $period['toDate'];\n $periodName = $this->formatReportPeriodName($frequency, $from, $to);\n $filenameSuffix = null;\n\n if ($customName) {\n if ($filenameSuffix) {\n $customName .= \" {$filenameSuffix}\";\n }\n\n $result = $this->sanitizeFileName(\"{$customName} - {$periodName}\");\n }\n\n $this->info($result);\n }\n\n public function calculateFromAndToDatePeriod(\n string $frequency,\n ?Carbon $fromDate = null,\n ?Carbon $toDate = null\n ): array {\n if ($frequency === self::FREQUENCY_ONE_OFF) {\n return [\n 'fromDate' => $fromDate,\n 'toDate' => $toDate,\n ];\n }\n\n $now = Carbon::now();\n\n return match ($frequency) {\n self::FREQUENCY_DAILY => [\n 'fromDate' => $now->copy()->subDay()->startOfDay(),\n 'toDate' => $now->copy()->subDay()->endOfDay(),\n ],\n self::FREQUENCY_WEEKLY => [\n 'fromDate' => $now->copy()->subWeeks(1)->startOfDay(),\n 'toDate' => $now->copy()->subDay()->endOfDay(),\n ],\n self::FREQUENCY_MONTHLY => [\n 'fromDate' => $now->copy()->subMonths(1)->startOfDay(),\n 'toDate' => $now->copy()->subDay()->endOfDay(),\n ],\n self::FREQUENCY_QUARTERLY => [\n 'fromDate' => $now->copy()->subMonths(3)->startOfDay(),\n 'toDate' => $now->copy()->subDay()->endOfDay(),\n ],\n default => throw new InvalidArgumentException(\"Unsupported frequency: {$frequency}\"),\n };\n }\n\n private function formatReportPeriodName(string $frequency, Carbon $from, Carbon $to): string\n {\n $fromYear = $from->format('Y');\n $toYear = $to->format('Y');\n $differentYears = $fromYear !== $toYear;\n\n switch ($frequency) {\n case self::FREQUENCY_DAILY:\n return $from->format('j M Y');\n\n case self::FREQUENCY_QUARTERLY:\n // 'Jan-Mar 2025' or 'Nov 2024-Jan 2025' if years differ\n $startMonth = $from->format('M');\n $endMonth = $to->copy()->subMonth();\n $endMonthName = $endMonth->format('M');\n $endMonthYear = $endMonth->format('Y');\n\n if ($differentYears) {\n return \"{$startMonth} {$fromYear} - {$endMonthName} {$endMonthYear}\";\n }\n\n return \"{$startMonth} - {$endMonthName} {$toYear}\";\n\n case self::FREQUENCY_MONTHLY:\n // 'May 2025' - monthly reports are always within the same year\n return $from->format('M Y');\n\n case self::FREQUENCY_WEEKLY:\n // '4 - 8 Aug 2025', '27 Oct - 3 Nov 2025', or '28 Dec 2024 - 3 Jan 2025' if years differ\n $startDay = $from->format('j');\n $endDay = $to->format('j');\n $startMonth = $from->format('M');\n $endMonth = $to->format('M');\n\n if ($differentYears) {\n return \"{$startDay} {$startMonth} {$fromYear} - {$endDay} {$endMonth} {$toYear}\";\n }\n\n if ($startMonth !== $endMonth) {\n return \"{$startDay} {$startMonth} - {$endDay} {$endMonth} {$toYear}\";\n }\n\n return \"{$startDay} - {$endDay} {$endMonth} {$toYear}\";\n\n case self::FREQUENCY_ONE_OFF:\n // '2 May-31 May 2025' or '15 Dec 2024-15 Jan 2025' if years differ\n $startDay = $from->format('j');\n $startMonth = $from->format('M');\n $endDay = $to->format('j');\n $endMonth = $to->format('M');\n\n // If same month and year, use a format like '2-31 May 2025'\n if ($startMonth === $endMonth && ! $differentYears) {\n return \"{$startDay} - {$endDay} {$startMonth} {$toYear}\";\n }\n\n // If different years, include both years\n if ($differentYears) {\n return \"{$startDay} {$startMonth} {$fromYear} - {$endDay} {$endMonth} {$toYear}\";\n }\n\n // Same year but different months\n return \"{$startDay} {$startMonth} - {$endDay} {$endMonth} {$toYear}\";\n\n default:\n // Default format for unknown frequencies\n return $from->format('j M Y') . ' - ' . $to->format('j M Y');\n }\n }\n\n public function sanitizeFileName(string $fileName): string\n {\n return str_replace(['/', '\\\\'], '-', $fileName);\n }\n\n private function getPayload(AutomatedReportsService $automatedReportsService)\n {\n $reportResult = AutomatedReportResult::find(269);\n $automatedReport = $reportResult->getReport();\n $activityIds = [1,2,3];\n $payload = $automatedReportsService->getAskJiminnyGenerateReportPayload(\n automatedReport: $automatedReport,\n reportResult: $reportResult,\n activityIds: $activityIds,\n );\n\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$payload ' . PHP_EOL . print_r($payload, true));\n }\n\n private function rateLimit()\n {\n $team = Team::find(2);\n $config = $team->getCrmConfiguration();\n\n $crmResolver = app(CrmOwnerResolver::class, [\n 'team' => $team,\n 'integrationAdmin' => $team->getOwner(),\n 'providerSlug' => $config->getProviderName(),\n ]);\n\n $crmService = $crmResolver->prepareCrmService();\n\n for ($i = 0 ; $i < 3; $i++) {\n// if ($i % 25 === 0) {\n// $this->info(\"Syncing opportunity {$i}\");\n $this->info(\"Matching contact {$i}\");\n// }\n// $crmService->syncOpportunity('374720564');\n $crmService->matchByName('Robot');\n }\n }\n\n private function simulateMatchActivityStorm(int $teamId = 2, int $count = 100): void\n {\n $team = Team::find($teamId);\n $config = $team->getCrmConfiguration();\n\n $activities = Activity::query()\n ->where('crm_configuration_id', $config->getId())\n ->orderByDesc('id')\n ->limit($count)\n ->get();\n\n $this->info(\"Dispatching {$activities->count()} MatchActivityCrmData jobs (portal={$config->getId()})\");\n\n foreach ($activities as $activity) {\n MatchActivityCrmData::dispatch($activity->getId(), $config, true);\n }\n\n $this->info('Done. Watch logs and run jiminny:debug observeRateLimit to inspect cache state.');\n }\n\n private function simulateVerifyTaskStorm(int $teamId = 2, int $count = 100): void\n {\n $activities = Activity::query()\n ->where('team_id', $teamId)\n ->whereNotNull('crm_provider_id')\n ->orderByDesc('id')\n ->limit($count)\n ->get();\n\n $this->info(\"Dispatching {$activities->count()} VerifyActivityCrmTaskJob jobs\");\n\n foreach ($activities as $activity) {\n VerifyActivityCrmTaskJob::dispatch($activity->getId());\n }\n\n $this->info('Done.');\n }\n\n private function observeRateLimitCache(int $teamId = 2): void\n {\n $team = Team::find($teamId);\n $config = $team->getCrmConfiguration();\n $key = sprintf('hubspot:ratelimit:portal:%d', $config->getId());\n\n $value = Redis::get($key);\n $ttl = Redis::ttl($key);\n\n $this->info(\"Redis key: {$key}\");\n $this->info('Value: ' . ($value ?? '(empty)'));\n $this->info(\"TTL: {$ttl}s\");\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Show Replace Field","depth":4,"bounds":{"left":0.5575133,"top":0.08060654,"width":0.008643617,"height":0.01915403},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Search History","depth":3,"bounds":{"left":0.57014626,"top":0.07980846,"width":0.00731383,"height":0.017557861},"on_screen":true,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"429","depth":4,"bounds":{"left":0.58111703,"top":0.07980846,"width":0.077792555,"height":0.015961692},"on_screen":true,"value":"429","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"New Line","depth":3,"bounds":{"left":0.66788566,"top":0.07980846,"width":0.00731383,"height":0.017557861},"on_screen":true,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Match Case","depth":3,"bounds":{"left":0.67785907,"top":0.07980846,"width":0.00731383,"height":0.017557861},"on_screen":true,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Words","depth":3,"bounds":{"left":0.68650264,"top":0.07980846,"width":0.00731383,"height":0.017557861},"on_screen":true,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Regex","depth":3,"bounds":{"left":0.69514626,"top":0.07980846,"width":0.00731383,"height":0.017557861},"on_screen":true,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Replace History","depth":3,"bounds":{"left":0.27027926,"top":1.0,"width":0.00731383,"height":0.0},"on_screen":false,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"Replace","depth":4,"on_screen":false,"role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"New Line","depth":3,"bounds":{"left":0.27027926,"top":1.0,"width":0.00731383,"height":0.0},"on_screen":false,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Preserve case","depth":3,"bounds":{"left":0.27027926,"top":1.0,"width":0.00731383,"height":0.0},"on_screen":false,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"1/2","depth":4,"bounds":{"left":0.7087766,"top":0.079010375,"width":0.025598405,"height":0.017557861},"on_screen":true,"role_description":"text"},{"role":"AXButton","text":"Previous Occurrence","depth":4,"bounds":{"left":0.734375,"top":0.07821229,"width":0.008643617,"height":0.01915403},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Occurrence","depth":4,"bounds":{"left":0.7430186,"top":0.07821229,"width":0.008643617,"height":0.01915403},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Filter Search Results","depth":4,"bounds":{"left":0.75166225,"top":0.07821229,"width":0.008643617,"height":0.01915403},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Open in Window, Multiple Cursors","depth":4,"bounds":{"left":0.7603058,"top":0.07821229,"width":0.008643617,"height":0.01915403},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXLink","text":"Click to highlight","depth":4,"on_screen":false,"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close","depth":4,"bounds":{"left":0.97539896,"top":0.07821229,"width":0.008643617,"height":0.01915403},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"174","depth":4,"bounds":{"left":0.96077126,"top":0.10933759,"width":0.011635638,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.9740692,"top":0.10774142,"width":0.00731383,"height":0.018355945},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.98138297,"top":0.10774142,"width":0.006981383,"height":0.018355945},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"[2026-05-11 11:06:08] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":612562,\"team_id\":2,\"email\":\"horen.kirazyan@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"c1949c5e-c0f4-431e-ba26-59d49408d871\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:08] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":612562,\"participants_processed\":2,\"exact_matches\":0,\"domain_matches\":1,\"best_match_found\":true} {\"correlation_id\":\"c1949c5e-c0f4-431e-ba26-59d49408d871\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:08] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612562} {\"correlation_id\":\"c1949c5e-c0f4-431e-ba26-59d49408d871\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:08] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612562,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"c1949c5e-c0f4-431e-ba26-59d49408d871\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:08] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":612562,\"remote_search\":true,\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89} {\"correlation_id\":\"c1949c5e-c0f4-431e-ba26-59d49408d871\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:09] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":612561,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89}} {\"correlation_id\":\"b715f531-04da-410f-be31-055aa1c13754\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:09] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612561} {\"correlation_id\":\"b715f531-04da-410f-be31-055aa1c13754\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:09] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612561,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"b715f531-04da-410f-be31-055aa1c13754\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:09] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":612561,\"participants\":[{\"id\":999780,\"user_id\":206,\"contact_id\":null,\"lead_id\":null},{\"id\":999781,\"user_id\":null,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"b715f531-04da-410f-be31-055aa1c13754\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:09] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"b715f531-04da-410f-be31-055aa1c13754\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:09] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"b715f531-04da-410f-be31-055aa1c13754\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:09] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"b715f531-04da-410f-be31-055aa1c13754\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:09] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"b715f531-04da-410f-be31-055aa1c13754\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:09] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":612561,\"team_id\":2,\"email\":\"horen.kirazyan@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"b715f531-04da-410f-be31-055aa1c13754\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:09] local.INFO: [Prospect match] Cache miss, calling the API {\"identifier_type\":\"email\",\"identifier\":\"447700174614.447782589921.OeREojLVnk@txt.staging.jiminny.com\"} {\"correlation_id\":\"b715f531-04da-410f-be31-055aa1c13754\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:10] local.INFO: [Hubspot] Failed to fetch contact {\"email\":\"447700174614.447782589921.OeREojLVnk@txt.staging.jiminny.com\",\"reason\":\"[404] Client error: `GET https://api.hubapi.com/crm/v3/objects/contacts/447700174614.447782589921.OeREojLVnk%40txt.staging.jiminny.com?properties=email%2Cfirstname%2Clastname%2Ccountry%2Cphone%2Cmobilephone%2Cjobtitle%2Chubspot_owner_id%2Cassociatedcompanyid%2Cphoto&archived=0&idProperty=email` resulted in a `404 Not Found` response\"} {\"correlation_id\":\"b715f531-04da-410f-be31-055aa1c13754\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:10] local.INFO: [Prospect match] API returned empty result, caching the miss with empty prospect data {\"identifier_type\":\"email\",\"identifier\":\"447700174614.447782589921.OeREojLVnk@txt.staging.jiminny.com\"} {\"correlation_id\":\"b715f531-04da-410f-be31-055aa1c13754\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:10] local.INFO: [Prospect match] Resolved company domain from email {\"email\":\"447700174614.447782589921.OeREojLVnk@txt.staging.jiminny.com\",\"domain\":\"jiminny.com\"} {\"correlation_id\":\"b715f531-04da-410f-be31-055aa1c13754\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:10] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":612561,\"participants_processed\":2,\"exact_matches\":0,\"domain_matches\":1,\"best_match_found\":true} {\"correlation_id\":\"b715f531-04da-410f-be31-055aa1c13754\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:10] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612561} {\"correlation_id\":\"b715f531-04da-410f-be31-055aa1c13754\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:10] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612561,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"b715f531-04da-410f-be31-055aa1c13754\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:10] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":612561,\"remote_search\":true,\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89} {\"correlation_id\":\"b715f531-04da-410f-be31-055aa1c13754\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:10] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":612560,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89}} {\"correlation_id\":\"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:10] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612560} {\"correlation_id\":\"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:10] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612560,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:10] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":612560,\"participants\":[{\"id\":999778,\"user_id\":null,\"contact_id\":null,\"lead_id\":null},{\"id\":999779,\"user_id\":206,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"447782589921@txt.staging.jiminny.com\"} {\"correlation_id\":\"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [Prospect match] cached empty result - no API calls, try next matching method {\"identifier_type\":\"email\",\"identifier\":\"447782589921@txt.staging.jiminny.com\"} {\"correlation_id\":\"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [Prospect match] Resolved company domain from email {\"email\":\"447782589921@txt.staging.jiminny.com\",\"domain\":\"jiminny.com\"} {\"correlation_id\":\"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":612560,\"team_id\":2,\"email\":\"horen.kirazyan@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":612560,\"participants_processed\":2,\"exact_matches\":0,\"domain_matches\":1,\"best_match_found\":true} {\"correlation_id\":\"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612560} {\"correlation_id\":\"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [ EsUpdateProcessManager ] Finished updating entities in ES {\"worker\":\"\",\"peak_memory\":\"99.73 MB\",\"elapsed_seconds\":0.74,\"update_target\":\"activities\",\"should_iterate_again\":false} {\"correlation_id\":\"38935ae1-fcb3-437e-9c9e-1b7415a0b7e1\",\"trace_id\":\"325a2f9b-9c66-4223-ac02-1a43723f4315\"}\n[2026-05-11 11:06:11] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612560,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":612560,\"remote_search\":true,\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89} {\"correlation_id\":\"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":612559,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":97,\"account_id\":69,\"opportunity_id\":5011,\"stage_id\":34}} {\"correlation_id\":\"4d59a59c-38a5-4a1d-9379-6ceaf67b747e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612559} {\"correlation_id\":\"4d59a59c-38a5-4a1d-9379-6ceaf67b747e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612559,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"4d59a59c-38a5-4a1d-9379-6ceaf67b747e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":612559,\"participants\":[{\"id\":999776,\"user_id\":206,\"contact_id\":null,\"lead_id\":null},{\"id\":999777,\"user_id\":null,\"contact_id\":97,\"lead_id\":null}]} {\"correlation_id\":\"4d59a59c-38a5-4a1d-9379-6ceaf67b747e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"4d59a59c-38a5-4a1d-9379-6ceaf67b747e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"4d59a59c-38a5-4a1d-9379-6ceaf67b747e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"4d59a59c-38a5-4a1d-9379-6ceaf67b747e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"4d59a59c-38a5-4a1d-9379-6ceaf67b747e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":612559,\"team_id\":2,\"email\":\"horen.kirazyan@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"4d59a59c-38a5-4a1d-9379-6ceaf67b747e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: ProspectCache - Searching DB for opportunity by owner {\"account_id\":69,\"contact_id\":97,\"owner_id\":206} {\"correlation_id\":\"4d59a59c-38a5-4a1d-9379-6ceaf67b747e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: ProspectCache - Fallback DB opportunity search {\"account_id\":69,\"contact_id\":97} {\"correlation_id\":\"4d59a59c-38a5-4a1d-9379-6ceaf67b747e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: ProspectCache - Opportunity DB search results {\"account_id\":69,\"contact_id\":97,\"opportunity_id\":5011} {\"correlation_id\":\"4d59a59c-38a5-4a1d-9379-6ceaf67b747e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"robinsoncrusoe@test.com\"} {\"correlation_id\":\"4d59a59c-38a5-4a1d-9379-6ceaf67b747e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":612559,\"participants_processed\":2,\"exact_matches\":1,\"domain_matches\":0,\"best_match_found\":true} {\"correlation_id\":\"4d59a59c-38a5-4a1d-9379-6ceaf67b747e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612559} {\"correlation_id\":\"4d59a59c-38a5-4a1d-9379-6ceaf67b747e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612559,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"4d59a59c-38a5-4a1d-9379-6ceaf67b747e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":612559,\"remote_search\":true,\"lead_id\":null,\"contact_id\":97,\"account_id\":69,\"opportunity_id\":5011,\"stage_id\":34} {\"correlation_id\":\"4d59a59c-38a5-4a1d-9379-6ceaf67b747e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:12] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":612558,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":97,\"account_id\":69,\"opportunity_id\":5011,\"stage_id\":34}} {\"correlation_id\":\"a105800d-d2f7-4772-a534-7791e262461c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:12] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612558} {\"correlation_id\":\"a105800d-d2f7-4772-a534-7791e262461c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:12] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612558,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"a105800d-d2f7-4772-a534-7791e262461c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:12] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":612558,\"participants\":[{\"id\":999774,\"user_id\":206,\"contact_id\":null,\"lead_id\":null},{\"id\":999775,\"user_id\":null,\"contact_id\":97,\"lead_id\":null}]} {\"correlation_id\":\"a105800d-d2f7-4772-a534-7791e262461c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:12] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"a105800d-d2f7-4772-a534-7791e262461c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:12] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"a105800d-d2f7-4772-a534-7791e262461c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:12] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"a105800d-d2f7-4772-a534-7791e262461c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:12] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"a105800d-d2f7-4772-a534-7791e262461c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:12] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":612558,\"team_id\":2,\"email\":\"horen.kirazyan@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"a105800d-d2f7-4772-a534-7791e262461c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:12] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"robinsoncrusoe@test.com\"} {\"correlation_id\":\"a105800d-d2f7-4772-a534-7791e262461c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:12] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":612558,\"participants_processed\":2,\"exact_matches\":1,\"domain_matches\":0,\"best_match_found\":true} {\"correlation_id\":\"a105800d-d2f7-4772-a534-7791e262461c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:12] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612558} {\"correlation_id\":\"a105800d-d2f7-4772-a534-7791e262461c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:12] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612558,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"a105800d-d2f7-4772-a534-7791e262461c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:12] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":612558,\"remote_search\":true,\"lead_id\":null,\"contact_id\":97,\"account_id\":69,\"opportunity_id\":5011,\"stage_id\":34} {\"correlation_id\":\"a105800d-d2f7-4772-a534-7791e262461c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:13] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":612557,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":97,\"account_id\":69,\"opportunity_id\":5011,\"stage_id\":34}} {\"correlation_id\":\"3ec57437-5796-4e44-8bf0-aaec149ac813\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:13] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612557} {\"correlation_id\":\"3ec57437-5796-4e44-8bf0-aaec149ac813\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:13] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612557,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"3ec57437-5796-4e44-8bf0-aaec149ac813\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:13] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":612557,\"participants\":[{\"id\":999772,\"user_id\":206,\"contact_id\":null,\"lead_id\":null},{\"id\":999773,\"user_id\":null,\"contact_id\":97,\"lead_id\":null}]} {\"correlation_id\":\"3ec57437-5796-4e44-8bf0-aaec149ac813\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:13] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"mailbox:skip-lists:refresh\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.727} {\"correlation_id\":\"5203040f-23be-43e6-8410-ba49ee1b38b4\",\"trace_id\":\"e787953d-9b78-4b63-a987-4f93f6892984\"}\n[2026-05-11 11:06:13] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"3ec57437-5796-4e44-8bf0-aaec149ac813\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:13] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"3ec57437-5796-4e44-8bf0-aaec149ac813\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:13] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"3ec57437-5796-4e44-8bf0-aaec149ac813\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:13] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"3ec57437-5796-4e44-8bf0-aaec149ac813\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:13] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":612557,\"team_id\":2,\"email\":\"horen.kirazyan@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"3ec57437-5796-4e44-8bf0-aaec149ac813\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:13] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"robinsoncrusoe@test.com\"} {\"correlation_id\":\"3ec57437-5796-4e44-8bf0-aaec149ac813\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:13] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":612557,\"participants_processed\":2,\"exact_matches\":1,\"domain_matches\":0,\"best_match_found\":true} {\"correlation_id\":\"3ec57437-5796-4e44-8bf0-aaec149ac813\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:13] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612557} {\"correlation_id\":\"3ec57437-5796-4e44-8bf0-aaec149ac813\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:13] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612557,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"3ec57437-5796-4e44-8bf0-aaec149ac813\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:13] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":612557,\"remote_search\":true,\"lead_id\":null,\"contact_id\":97,\"account_id\":69,\"opportunity_id\":5011,\"stage_id\":34} {\"correlation_id\":\"3ec57437-5796-4e44-8bf0-aaec149ac813\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:13] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage for command {\"command\":\"mailbox:skip-lists:refresh\",\"memoryBeforeCommandInMb\":60.0,\"memoryAfterCommandInMB\":60.0,\"memoryPeakBeforeCommandInMb\":99.727,\"memoryPeakAfterCommandInMB\":99.727} {\"correlation_id\":\"5203040f-23be-43e6-8410-ba49ee1b38b4\",\"trace_id\":\"e787953d-9b78-4b63-a987-4f93f6892984\"}\n[2026-05-11 11:06:13] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":612556,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":97,\"account_id\":69,\"opportunity_id\":5011,\"stage_id\":34}} {\"correlation_id\":\"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:13] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612556} {\"correlation_id\":\"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:13] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612556,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:13] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":612556,\"participants\":[{\"id\":999770,\"user_id\":206,\"contact_id\":null,\"lead_id\":null},{\"id\":999771,\"user_id\":null,\"contact_id\":97,\"lead_id\":null}]} {\"correlation_id\":\"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:13] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:13] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:13] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:13] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":612556,\"team_id\":2,\"email\":\"horen.kirazyan@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"robinsoncrusoe@test.com\"} {\"correlation_id\":\"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":612556,\"participants_processed\":2,\"exact_matches\":1,\"domain_matches\":0,\"best_match_found\":true} {\"correlation_id\":\"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612556} {\"correlation_id\":\"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612556,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":612556,\"remote_search\":true,\"lead_id\":null,\"contact_id\":97,\"account_id\":69,\"opportunity_id\":5011,\"stage_id\":34} {\"correlation_id\":\"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":612555,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":97,\"account_id\":69,\"opportunity_id\":5011,\"stage_id\":34}} {\"correlation_id\":\"3e9d1a70-5491-44ae-8f28-5cb9b60e1859\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612555} {\"correlation_id\":\"3e9d1a70-5491-44ae-8f28-5cb9b60e1859\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612555,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"3e9d1a70-5491-44ae-8f28-5cb9b60e1859\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":612555,\"participants\":[{\"id\":999768,\"user_id\":206,\"contact_id\":null,\"lead_id\":null},{\"id\":999769,\"user_id\":null,\"contact_id\":97,\"lead_id\":null}]} {\"correlation_id\":\"3e9d1a70-5491-44ae-8f28-5cb9b60e1859\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"3e9d1a70-5491-44ae-8f28-5cb9b60e1859\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"3e9d1a70-5491-44ae-8f28-5cb9b60e1859\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"3e9d1a70-5491-44ae-8f28-5cb9b60e1859\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"3e9d1a70-5491-44ae-8f28-5cb9b60e1859\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":612555,\"team_id\":2,\"email\":\"horen.kirazyan@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"3e9d1a70-5491-44ae-8f28-5cb9b60e1859\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"robinsoncrusoe@test.com\"} {\"correlation_id\":\"3e9d1a70-5491-44ae-8f28-5cb9b60e1859\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":612555,\"participants_processed\":2,\"exact_matches\":1,\"domain_matches\":0,\"best_match_found\":true} {\"correlation_id\":\"3e9d1a70-5491-44ae-8f28-5cb9b60e1859\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612555} {\"correlation_id\":\"3e9d1a70-5491-44ae-8f28-5cb9b60e1859\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612555,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"3e9d1a70-5491-44ae-8f28-5cb9b60e1859\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":612555,\"remote_search\":true,\"lead_id\":null,\"contact_id\":97,\"account_id\":69,\"opportunity_id\":5011,\"stage_id\":34} {\"correlation_id\":\"3e9d1a70-5491-44ae-8f28-5cb9b60e1859\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":612554,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":97,\"account_id\":69,\"opportunity_id\":5011,\"stage_id\":34}} {\"correlation_id\":\"7101e979-3a7e-4b2c-bf04-dc6a24ba1356\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612554} {\"correlation_id\":\"7101e979-3a7e-4b2c-bf04-dc6a24ba1356\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612554,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"7101e979-3a7e-4b2c-bf04-dc6a24ba1356\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":612554,\"participants\":[{\"id\":999766,\"user_id\":206,\"contact_id\":null,\"lead_id\":null},{\"id\":999767,\"user_id\":null,\"contact_id\":97,\"lead_id\":null}]} {\"correlation_id\":\"7101e979-3a7e-4b2c-bf04-dc6a24ba1356\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"7101e979-3a7e-4b2c-bf04-dc6a24ba1356\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"7101e979-3a7e-4b2c-bf04-dc6a24ba1356\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"7101e979-3a7e-4b2c-bf04-dc6a24ba1356\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"7101e979-3a7e-4b2c-bf04-dc6a24ba1356\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":612554,\"team_id\":2,\"email\":\"horen.kirazyan@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"7101e979-3a7e-4b2c-bf04-dc6a24ba1356\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"robinsoncrusoe@test.com\"} {\"correlation_id\":\"7101e979-3a7e-4b2c-bf04-dc6a24ba1356\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":612554,\"participants_processed\":2,\"exact_matches\":1,\"domain_matches\":0,\"best_match_found\":true} {\"correlation_id\":\"7101e979-3a7e-4b2c-bf04-dc6a24ba1356\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612554} {\"correlation_id\":\"7101e979-3a7e-4b2c-bf04-dc6a24ba1356\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612554,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"7101e979-3a7e-4b2c-bf04-dc6a24ba1356\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":612554,\"remote_search\":true,\"lead_id\":null,\"contact_id\":97,\"account_id\":69,\"opportunity_id\":5011,\"stage_id\":34} {\"correlation_id\":\"7101e979-3a7e-4b2c-bf04-dc6a24ba1356\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":612553,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":97,\"account_id\":69,\"opportunity_id\":5011,\"stage_id\":34}} {\"correlation_id\":\"346dc229-757b-496c-aceb-d2ddb367c7d6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612553} {\"correlation_id\":\"346dc229-757b-496c-aceb-d2ddb367c7d6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612553,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"346dc229-757b-496c-aceb-d2ddb367c7d6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":612553,\"participants\":[{\"id\":999764,\"user_id\":206,\"contact_id\":null,\"lead_id\":null},{\"id\":999765,\"user_id\":null,\"contact_id\":97,\"lead_id\":null}]} {\"correlation_id\":\"346dc229-757b-496c-aceb-d2ddb367c7d6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"346dc229-757b-496c-aceb-d2ddb367c7d6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"346dc229-757b-496c-aceb-d2ddb367c7d6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"346dc229-757b-496c-aceb-d2ddb367c7d6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"346dc229-757b-496c-aceb-d2ddb367c7d6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":612553,\"team_id\":2,\"email\":\"horen.kirazyan@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"346dc229-757b-496c-aceb-d2ddb367c7d6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"robinsoncrusoe@test.com\"} {\"correlation_id\":\"346dc229-757b-496c-aceb-d2ddb367c7d6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":612553,\"participants_processed\":2,\"exact_matches\":1,\"domain_matches\":0,\"best_match_found\":true} {\"correlation_id\":\"346dc229-757b-496c-aceb-d2ddb367c7d6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612553} {\"correlation_id\":\"346dc229-757b-496c-aceb-d2ddb367c7d6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612553,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"346dc229-757b-496c-aceb-d2ddb367c7d6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":612553,\"remote_search\":true,\"lead_id\":null,\"contact_id\":97,\"account_id\":69,\"opportunity_id\":5011,\"stage_id\":34} {\"correlation_id\":\"346dc229-757b-496c-aceb-d2ddb367c7d6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":612552,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":97,\"account_id\":69,\"opportunity_id\":5011,\"stage_id\":34}} {\"correlation_id\":\"56ef8656-0bfa-4f11-a245-9254b8179555\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612552} {\"correlation_id\":\"56ef8656-0bfa-4f11-a245-9254b8179555\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612552,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"56ef8656-0bfa-4f11-a245-9254b8179555\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":612552,\"participants\":[{\"id\":999762,\"user_id\":206,\"contact_id\":null,\"lead_id\":null},{\"id\":999763,\"user_id\":null,\"contact_id\":97,\"lead_id\":null}]} {\"correlation_id\":\"56ef8656-0bfa-4f11-a245-9254b8179555\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"56ef8656-0bfa-4f11-a245-9254b8179555\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"56ef8656-0bfa-4f11-a245-9254b8179555\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"56ef8656-0bfa-4f11-a245-9254b8179555\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"56ef8656-0bfa-4f11-a245-9254b8179555\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":612552,\"team_id\":2,\"email\":\"horen.kirazyan@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"56ef8656-0bfa-4f11-a245-9254b8179555\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"robinsoncrusoe@test.com\"} {\"correlation_id\":\"56ef8656-0bfa-4f11-a245-9254b8179555\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":612552,\"participants_processed\":2,\"exact_matches\":1,\"domain_matches\":0,\"best_match_found\":true} {\"correlation_id\":\"56ef8656-0bfa-4f11-a245-9254b8179555\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612552} {\"correlation_id\":\"56ef8656-0bfa-4f11-a245-9254b8179555\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612552,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"56ef8656-0bfa-4f11-a245-9254b8179555\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":612552,\"remote_search\":true,\"lead_id\":null,\"contact_id\":97,\"account_id\":69,\"opportunity_id\":5011,\"stage_id\":34} {\"correlation_id\":\"56ef8656-0bfa-4f11-a245-9254b8179555\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":612551,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":97,\"account_id\":69,\"opportunity_id\":5011,\"stage_id\":34}} {\"correlation_id\":\"df5e35c3-b4ae-4eba-9108-cd30ac152335\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612551} {\"correlation_id\":\"df5e35c3-b4ae-4eba-9108-cd30ac152335\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612551,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"df5e35c3-b4ae-4eba-9108-cd30ac152335\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":612551,\"participants\":[{\"id\":999760,\"user_id\":206,\"contact_id\":null,\"lead_id\":null},{\"id\":999761,\"user_id\":null,\"contact_id\":97,\"lead_id\":null}]} {\"correlation_id\":\"df5e35c3-b4ae-4eba-9108-cd30ac152335\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"df5e35c3-b4ae-4eba-9108-cd30ac152335\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"df5e35c3-b4ae-4eba-9108-cd30ac152335\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"df5e35c3-b4ae-4eba-9108-cd30ac152335\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"df5e35c3-b4ae-4eba-9108-cd30ac152335\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":612551,\"team_id\":2,\"email\":\"horen.kirazyan@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"df5e35c3-b4ae-4eba-9108-cd30ac152335\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"robinsoncrusoe@test.com\"} {\"correlation_id\":\"df5e35c3-b4ae-4eba-9108-cd30ac152335\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":612551,\"participants_processed\":2,\"exact_matches\":1,\"domain_matches\":0,\"best_match_found\":true} {\"correlation_id\":\"df5e35c3-b4ae-4eba-9108-cd30ac152335\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612551} {\"correlation_id\":\"df5e35c3-b4ae-4eba-9108-cd30ac152335\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612551,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"df5e35c3-b4ae-4eba-9108-cd30ac152335\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":612551,\"remote_search\":true,\"lead_id\":null,\"contact_id\":97,\"account_id\":69,\"opportunity_id\":5011,\"stage_id\":34} {\"correlation_id\":\"df5e35c3-b4ae-4eba-9108-cd30ac152335\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":612550,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":97,\"account_id\":69,\"opportunity_id\":5011,\"stage_id\":34}} {\"correlation_id\":\"64768905-983e-4f4e-a368-901931486f34\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612550} {\"correlation_id\":\"64768905-983e-4f4e-a368-901931486f34\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612550,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"64768905-983e-4f4e-a368-901931486f34\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":612550,\"participants\":[{\"id\":999758,\"user_id\":206,\"contact_id\":null,\"lead_id\":null},{\"id\":999759,\"user_id\":null,\"contact_id\":97,\"lead_id\":null}]} {\"correlation_id\":\"64768905-983e-4f4e-a368-901931486f34\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"64768905-983e-4f4e-a368-901931486f34\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"64768905-983e-4f4e-a368-901931486f34\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"64768905-983e-4f4e-a368-901931486f34\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"64768905-983e-4f4e-a368-901931486f34\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":612550,\"team_id\":2,\"email\":\"horen.kirazyan@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"64768905-983e-4f4e-a368-901931486f34\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"robinsoncrusoe@test.com\"} {\"correlation_id\":\"64768905-983e-4f4e-a368-901931486f34\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":612550,\"participants_processed\":2,\"exact_matches\":1,\"domain_matches\":0,\"best_match_found\":true} {\"correlation_id\":\"64768905-983e-4f4e-a368-901931486f34\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612550} {\"correlation_id\":\"64768905-983e-4f4e-a368-901931486f34\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612550,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"64768905-983e-4f4e-a368-901931486f34\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":612550,\"remote_search\":true,\"lead_id\":null,\"contact_id\":97,\"account_id\":69,\"opportunity_id\":5011,\"stage_id\":34} {\"correlation_id\":\"64768905-983e-4f4e-a368-901931486f34\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":612549,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":97,\"account_id\":69,\"opportunity_id\":5011,\"stage_id\":34}} {\"correlation_id\":\"97c9609f-bef0-4e95-8301-9f5549d29c1e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612549} {\"correlation_id\":\"97c9609f-bef0-4e95-8301-9f5549d29c1e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612549,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"97c9609f-bef0-4e95-8301-9f5549d29c1e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":612549,\"participants\":[{\"id\":999756,\"user_id\":206,\"contact_id\":null,\"lead_id\":null},{\"id\":999757,\"user_id\":null,\"contact_id\":97,\"lead_id\":null}]} {\"correlation_id\":\"97c9609f-bef0-4e95-8301-9f5549d29c1e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"97c9609f-bef0-4e95-8301-9f5549d29c1e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"97c9609f-bef0-4e95-8301-9f5549d29c1e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"97c9609f-bef0-4e95-8301-9f5549d29c1e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"97c9609f-bef0-4e95-8301-9f5549d29c1e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":612549,\"team_id\":2,\"email\":\"horen.kirazyan@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"97c9609f-bef0-4e95-8301-9f5549d29c1e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"robinsoncrusoe@test.com\"} {\"correlation_id\":\"97c9609f-bef0-4e95-8301-9f5549d29c1e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":612549,\"participants_processed\":2,\"exact_matches\":1,\"domain_matches\":0,\"best_match_found\":true} {\"correlation_id\":\"97c9609f-bef0-4e95-8301-9f5549d29c1e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612549} {\"correlation_id\":\"97c9609f-bef0-4e95-8301-9f5549d29c1e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612549,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"97c9609f-bef0-4e95-8301-9f5549d29c1e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":612549,\"remote_search\":true,\"lead_id\":null,\"contact_id\":97,\"account_id\":69,\"opportunity_id\":5011,\"stage_id\":34} {\"correlation_id\":\"97c9609f-bef0-4e95-8301-9f5549d29c1e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":612365,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":4491,\"account_id\":243,\"opportunity_id\":276,\"stage_id\":36}} {\"correlation_id\":\"7761b4e0-c2d4-4805-8d1d-26728cbffb07\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612365} {\"correlation_id\":\"7761b4e0-c2d4-4805-8d1d-26728cbffb07\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612365,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"7761b4e0-c2d4-4805-8d1d-26728cbffb07\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":612365,\"participants\":[{\"id\":999563,\"user_id\":null,\"contact_id\":4491,\"lead_id\":null},{\"id\":999564,\"user_id\":206,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"7761b4e0-c2d4-4805-8d1d-26728cbffb07\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"7761b4e0-c2d4-4805-8d1d-26728cbffb07\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"7761b4e0-c2d4-4805-8d1d-26728cbffb07\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"7761b4e0-c2d4-4805-8d1d-26728cbffb07\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"7761b4e0-c2d4-4805-8d1d-26728cbffb07\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"horencho@gmail.com\"} {\"correlation_id\":\"7761b4e0-c2d4-4805-8d1d-26728cbffb07\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":612365,\"team_id\":2,\"email\":\"horen.kirazyan@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"7761b4e0-c2d4-4805-8d1d-26728cbffb07\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":612365,\"participants_processed\":2,\"exact_matches\":1,\"domain_matches\":0,\"best_match_found\":true} {\"correlation_id\":\"7761b4e0-c2d4-4805-8d1d-26728cbffb07\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612365} {\"correlation_id\":\"7761b4e0-c2d4-4805-8d1d-26728cbffb07\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612365,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"7761b4e0-c2d4-4805-8d1d-26728cbffb07\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":612365,\"remote_search\":true,\"lead_id\":null,\"contact_id\":4491,\"account_id\":243,\"opportunity_id\":276,\"stage_id\":36} {\"correlation_id\":\"7761b4e0-c2d4-4805-8d1d-26728cbffb07\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":612360,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":4491,\"account_id\":243,\"opportunity_id\":276,\"stage_id\":36}} {\"correlation_id\":\"b8b554cd-4830-4ac5-be75-595cc7eb6ca8\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612360} {\"correlation_id\":\"b8b554cd-4830-4ac5-be75-595cc7eb6ca8\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612360,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"b8b554cd-4830-4ac5-be75-595cc7eb6ca8\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":612360,\"participants\":[{\"id\":999552,\"user_id\":null,\"contact_id\":4491,\"lead_id\":null},{\"id\":999553,\"user_id\":206,\"contact_id\":null,\"lead_id\":null},{\"id\":999565,\"user_id\":null,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"b8b554cd-4830-4ac5-be75-595cc7eb6ca8\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"b8b554cd-4830-4ac5-be75-595cc7eb6ca8\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"b8b554cd-4830-4ac5-be75-595cc7eb6ca8\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"b8b554cd-4830-4ac5-be75-595cc7eb6ca8\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"b8b554cd-4830-4ac5-be75-595cc7eb6ca8\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"horencho@gmail.com\"} {\"correlation_id\":\"b8b554cd-4830-4ac5-be75-595cc7eb6ca8\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":612360,\"team_id\":2,\"email\":\"horen.kirazyan@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"b8b554cd-4830-4ac5-be75-595cc7eb6ca8\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [Hubspot] Pagination completed {\"team_id\":2,\"endpoint\":\"https://api.hubapi.com/crm/v3/objects/contact/search\",\"total_requests\":1,\"total_records_fetched\":0,\"total_elapsed_seconds\":0.31,\"average_seconds_per_request\":0.31} {\"correlation_id\":\"b8b554cd-4830-4ac5-be75-595cc7eb6ca8\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":612360,\"participants_processed\":3,\"exact_matches\":1,\"domain_matches\":0,\"best_match_found\":true} {\"correlation_id\":\"b8b554cd-4830-4ac5-be75-595cc7eb6ca8\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612360} {\"correlation_id\":\"b8b554cd-4830-4ac5-be75-595cc7eb6ca8\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612360,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"b8b554cd-4830-4ac5-be75-595cc7eb6ca8\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":612360,\"remote_search\":true,\"lead_id\":null,\"contact_id\":4491,\"account_id\":243,\"opportunity_id\":276,\"stage_id\":36} {\"correlation_id\":\"b8b554cd-4830-4ac5-be75-595cc7eb6ca8\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":612340,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":4491,\"account_id\":243,\"opportunity_id\":276,\"stage_id\":36}} {\"correlation_id\":\"9a47ab79-dbb3-46e2-ad27-3a5120c080ef\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612340} {\"correlation_id\":\"9a47ab79-dbb3-46e2-ad27-3a5120c080ef\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612340,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"9a47ab79-dbb3-46e2-ad27-3a5120c080ef\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":612340,\"participants\":[{\"id\":999516,\"user_id\":null,\"contact_id\":4491,\"lead_id\":null},{\"id\":999517,\"user_id\":206,\"contact_id\":null,\"lead_id\":null},{\"id\":999518,\"user_id\":null,\"contact_id\":null,\"lead_id\":null},{\"id\":999519,\"user_id\":null,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"9a47ab79-dbb3-46e2-ad27-3a5120c080ef\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"9a47ab79-dbb3-46e2-ad27-3a5120c080ef\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"9a47ab79-dbb3-46e2-ad27-3a5120c080ef\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"9a47ab79-dbb3-46e2-ad27-3a5120c080ef\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"9a47ab79-dbb3-46e2-ad27-3a5120c080ef\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"horencho@gmail.com\"} {\"correlation_id\":\"9a47ab79-dbb3-46e2-ad27-3a5120c080ef\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":612340,\"team_id\":2,\"email\":\"horen.kirazyan@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"9a47ab79-dbb3-46e2-ad27-3a5120c080ef\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [Hubspot] Pagination completed {\"team_id\":2,\"endpoint\":\"https://api.hubapi.com/crm/v3/objects/contact/search\",\"total_requests\":1,\"total_records_fetched\":0,\"total_elapsed_seconds\":0.24,\"average_seconds_per_request\":0.24} {\"correlation_id\":\"9a47ab79-dbb3-46e2-ad27-3a5120c080ef\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":612340,\"participants_processed\":4,\"exact_matches\":1,\"domain_matches\":0,\"best_match_found\":true} {\"correlation_id\":\"9a47ab79-dbb3-46e2-ad27-3a5120c080ef\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [ EsUpdateProcessManager ] Finished updating entities in ES {\"worker\":\"\",\"peak_memory\":\"99.73 MB\",\"elapsed_seconds\":0.45,\"update_target\":\"activities\",\"should_iterate_again\":false} {\"correlation_id\":\"38935ae1-fcb3-437e-9c9e-1b7415a0b7e1\",\"trace_id\":\"325a2f9b-9c66-4223-ac02-1a43723f4315\"}\n[2026-05-11 11:06:16] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612340} {\"correlation_id\":\"9a47ab79-dbb3-46e2-ad27-3a5120c080ef\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612340,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"9a47ab79-dbb3-46e2-ad27-3a5120c080ef\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":612340,\"remote_search\":true,\"lead_id\":null,\"contact_id\":4491,\"account_id\":243,\"opportunity_id\":276,\"stage_id\":36} {\"correlation_id\":\"9a47ab79-dbb3-46e2-ad27-3a5120c080ef\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":612339,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":4491,\"account_id\":243,\"opportunity_id\":276,\"stage_id\":36}} {\"correlation_id\":\"eae16400-35f9-4885-b5fc-edb8872350d4\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612339} {\"correlation_id\":\"eae16400-35f9-4885-b5fc-edb8872350d4\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612339,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"eae16400-35f9-4885-b5fc-edb8872350d4\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":612339,\"participants\":[{\"id\":999514,\"user_id\":null,\"contact_id\":4491,\"lead_id\":null},{\"id\":999515,\"user_id\":206,\"contact_id\":null,\"lead_id\":null},{\"id\":999540,\"user_id\":null,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"eae16400-35f9-4885-b5fc-edb8872350d4\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"eae16400-35f9-4885-b5fc-edb8872350d4\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"eae16400-35f9-4885-b5fc-edb8872350d4\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"eae16400-35f9-4885-b5fc-edb8872350d4\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"eae16400-35f9-4885-b5fc-edb8872350d4\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"horencho@gmail.com\"} {\"correlation_id\":\"eae16400-35f9-4885-b5fc-edb8872350d4\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":612339,\"team_id\":2,\"email\":\"horen.kirazyan@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"eae16400-35f9-4885-b5fc-edb8872350d4\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":612339,\"participants_processed\":3,\"exact_matches\":1,\"domain_matches\":0,\"best_match_found\":true} {\"correlation_id\":\"eae16400-35f9-4885-b5fc-edb8872350d4\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612339} {\"correlation_id\":\"eae16400-35f9-4885-b5fc-edb8872350d4\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612339,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"eae16400-35f9-4885-b5fc-edb8872350d4\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":612339,\"remote_search\":true,\"lead_id\":null,\"contact_id\":4491,\"account_id\":243,\"opportunity_id\":276,\"stage_id\":36} {\"correlation_id\":\"eae16400-35f9-4885-b5fc-edb8872350d4\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":612336,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":4491,\"account_id\":243,\"opportunity_id\":276,\"stage_id\":36}} {\"correlation_id\":\"1028998b-3369-4f2f-b7cb-e4ef79db71a5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612336} {\"correlation_id\":\"1028998b-3369-4f2f-b7cb-e4ef79db71a5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612336,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"1028998b-3369-4f2f-b7cb-e4ef79db71a5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":612336,\"participants\":[{\"id\":999508,\"user_id\":null,\"contact_id\":4491,\"lead_id\":null},{\"id\":999509,\"user_id\":206,\"contact_id\":null,\"lead_id\":null},{\"id\":999512,\"user_id\":null,\"contact_id\":null,\"lead_id\":null},{\"id\":999513,\"user_id\":null,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"1028998b-3369-4f2f-b7cb-e4ef79db71a5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"1028998b-3369-4f2f-b7cb-e4ef79db71a5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"1028998b-3369-4f2f-b7cb-e4ef79db71a5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"1028998b-3369-4f2f-b7cb-e4ef79db71a5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"1028998b-3369-4f2f-b7cb-e4ef79db71a5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"horencho@gmail.com\"} {\"correlation_id\":\"1028998b-3369-4f2f-b7cb-e4ef79db71a5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":612336,\"team_id\":2,\"email\":\"horen.kirazyan@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"1028998b-3369-4f2f-b7cb-e4ef79db71a5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":612336,\"participants_processed\":4,\"exact_matches\":1,\"domain_matches\":0,\"best_match_found\":true} {\"correlation_id\":\"1028998b-3369-4f2f-b7cb-e4ef79db71a5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612336} {\"correlation_id\":\"1028998b-3369-4f2f-b7cb-e4ef79db71a5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612336,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"1028998b-3369-4f2f-b7cb-e4ef79db71a5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":612336,\"remote_search\":true,\"lead_id\":null,\"contact_id\":4491,\"account_id\":243,\"opportunity_id\":276,\"stage_id\":36} {\"correlation_id\":\"1028998b-3369-4f2f-b7cb-e4ef79db71a5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":612183,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":4487,\"account_id\":244,\"opportunity_id\":299,\"stage_id\":36}} {\"correlation_id\":\"135ad495-9ac2-4ae2-9428-66f40f3a4731\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612183} {\"correlation_id\":\"135ad495-9ac2-4ae2-9428-66f40f3a4731\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612183,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"135ad495-9ac2-4ae2-9428-66f40f3a4731\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":612183,\"participants\":[{\"id\":999227,\"user_id\":null,\"contact_id\":4487,\"lead_id\":null},{\"id\":999228,\"user_id\":261,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"135ad495-9ac2-4ae2-9428-66f40f3a4731\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"135ad495-9ac2-4ae2-9428-66f40f3a4731\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"135ad495-9ac2-4ae2-9428-66f40f3a4731\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"135ad495-9ac2-4ae2-9428-66f40f3a4731\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"135ad495-9ac2-4ae2-9428-66f40f3a4731\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: ProspectCache - Searching DB for opportunity by owner {\"account_id\":244,\"contact_id\":4487,\"owner_id\":261} {\"correlation_id\":\"135ad495-9ac2-4ae2-9428-66f40f3a4731\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: ProspectCache - Opportunity DB search results {\"account_id\":244,\"contact_id\":4487,\"opportunity_id\":299} {\"correlation_id\":\"135ad495-9ac2-4ae2-9428-66f40f3a4731\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"robinson@crusoe.com\"} {\"correlation_id\":\"135ad495-9ac2-4ae2-9428-66f40f3a4731\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":612183,\"team_id\":2,\"email\":\"adelina.petrova@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"135ad495-9ac2-4ae2-9428-66f40f3a4731\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":612183,\"participants_processed\":2,\"exact_matches\":1,\"domain_matches\":0,\"best_match_found\":true} {\"correlation_id\":\"135ad495-9ac2-4ae2-9428-66f40f3a4731\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612183} {\"correlation_id\":\"135ad495-9ac2-4ae2-9428-66f40f3a4731\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612183,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"135ad495-9ac2-4ae2-9428-66f40f3a4731\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":612183,\"remote_search\":true,\"lead_id\":null,\"contact_id\":4487,\"account_id\":244,\"opportunity_id\":299,\"stage_id\":36} {\"correlation_id\":\"135ad495-9ac2-4ae2-9428-66f40f3a4731\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":612182,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":4487,\"account_id\":244,\"opportunity_id\":299,\"stage_id\":36}} {\"correlation_id\":\"9a2d51b0-64bc-41ea-86b1-8f3bcbb65a89\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612182} {\"correlation_id\":\"9a2d51b0-64bc-41ea-86b1-8f3bcbb65a89\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612182,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"9a2d51b0-64bc-41ea-86b1-8f3bcbb65a89\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":612182,\"participants\":[{\"id\":999225,\"user_id\":null,\"contact_id\":4487,\"lead_id\":null},{\"id\":999226,\"user_id\":261,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"9a2d51b0-64bc-41ea-86b1-8f3bcbb65a89\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"mailbox:batch:process\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.727} {\"correlation_id\":\"dceccba8-9e9c-476e-993d-809067884ebe\",\"trace_id\":\"66f8bba8-1712-464b-b25a-418f16736be2\"}\n[2026-05-11 11:06:17] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"9a2d51b0-64bc-41ea-86b1-8f3bcbb65a89\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"9a2d51b0-64bc-41ea-86b1-8f3bcbb65a89\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"9a2d51b0-64bc-41ea-86b1-8f3bcbb65a89\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"9a2d51b0-64bc-41ea-86b1-8f3bcbb65a89\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"robinson@crusoe.com\"} {\"correlation_id\":\"9a2d51b0-64bc-41ea-86b1-8f3bcbb65a89\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":612182,\"team_id\":2,\"email\":\"adelina.petrova@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"9a2d51b0-64bc-41ea-86b1-8f3bcbb65a89\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":612182,\"participants_processed\":2,\"exact_matches\":1,\"domain_matches\":0,\"best_match_found\":true} {\"correlation_id\":\"9a2d51b0-64bc-41ea-86b1-8f3bcbb65a89\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612182} {\"correlation_id\":\"9a2d51b0-64bc-41ea-86b1-8f3bcbb65a89\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612182,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"9a2d51b0-64bc-41ea-86b1-8f3bcbb65a89\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [EmailSchedule] STARTING batch process {\"host\":\"docker_lamp_1\"} {\"correlation_id\":\"dceccba8-9e9c-476e-993d-809067884ebe\",\"trace_id\":\"66f8bba8-1712-464b-b25a-418f16736be2\"}\n[2026-05-11 11:06:17] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":612182,\"remote_search\":true,\"lead_id\":null,\"contact_id\":4487,\"account_id\":244,\"opportunity_id\":299,\"stage_id\":36} {\"correlation_id\":\"9a2d51b0-64bc-41ea-86b1-8f3bcbb65a89\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [EmailSchedule] FINISHED batch process {\"host\":\"docker_lamp_1\",\"processed\":0} {\"correlation_id\":\"dceccba8-9e9c-476e-993d-809067884ebe\",\"trace_id\":\"66f8bba8-1712-464b-b25a-418f16736be2\"}\n[2026-05-11 11:06:17] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage for command {\"command\":\"mailbox:batch:process\",\"memoryBeforeCommandInMb\":60.0,\"memoryAfterCommandInMB\":60.0,\"memoryPeakBeforeCommandInMb\":99.727,\"memoryPeakAfterCommandInMB\":99.727} {\"correlation_id\":\"dceccba8-9e9c-476e-993d-809067884ebe\",\"trace_id\":\"66f8bba8-1712-464b-b25a-418f16736be2\"}\n[2026-05-11 11:06:17] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":612181,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":4487,\"account_id\":244,\"opportunity_id\":299,\"stage_id\":36}} {\"correlation_id\":\"8ff40f83-337f-432a-b950-e35779bb59bf\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612181} {\"correlation_id\":\"8ff40f83-337f-432a-b950-e35779bb59bf\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612181,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"8ff40f83-337f-432a-b950-e35779bb59bf\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":612181,\"participants\":[{\"id\":999223,\"user_id\":null,\"contact_id\":4487,\"lead_id\":null},{\"id\":999224,\"user_id\":261,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"8ff40f83-337f-432a-b950-e35779bb59bf\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"8ff40f83-337f-432a-b950-e35779bb59bf\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"8ff40f83-337f-432a-b950-e35779bb59bf\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"8ff40f83-337f-432a-b950-e35779bb59bf\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"8ff40f83-337f-432a-b950-e35779bb59bf\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"robinson@crusoe.com\"} {\"correlation_id\":\"8ff40f83-337f-432a-b950-e35779bb59bf\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":612181,\"team_id\":2,\"email\":\"adelina.petrova@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"8ff40f83-337f-432a-b950-e35779bb59bf\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":612181,\"participants_processed\":2,\"exact_matches\":1,\"domain_matches\":0,\"best_match_found\":true} {\"correlation_id\":\"8ff40f83-337f-432a-b950-e35779bb59bf\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612181} {\"correlation_id\":\"8ff40f83-337f-432a-b950-e35779bb59bf\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612181,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"8ff40f83-337f-432a-b950-e35779bb59bf\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":612181,\"remote_search\":true,\"lead_id\":null,\"contact_id\":4487,\"account_id\":244,\"opportunity_id\":299,\"stage_id\":36} {\"correlation_id\":\"8ff40f83-337f-432a-b950-e35779bb59bf\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":612180,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":4487,\"account_id\":244,\"opportunity_id\":299,\"stage_id\":36}} {\"correlation_id\":\"03f05b91-f803-45c9-b4bf-6a5f8ddf4a2c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612180} {\"correlation_id\":\"03f05b91-f803-45c9-b4bf-6a5f8ddf4a2c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612180,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"03f05b91-f803-45c9-b4bf-6a5f8ddf4a2c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":612180,\"participants\":[{\"id\":999221,\"user_id\":null,\"contact_id\":4487,\"lead_id\":null},{\"id\":999222,\"user_id\":261,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"03f05b91-f803-45c9-b4bf-6a5f8ddf4a2c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"03f05b91-f803-45c9-b4bf-6a5f8ddf4a2c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"03f05b91-f803-45c9-b4bf-6a5f8ddf4a2c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"03f05b91-f803-45c9-b4bf-6a5f8ddf4a2c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"03f05b91-f803-45c9-b4bf-6a5f8ddf4a2c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"robinson@crusoe.com\"} {\"correlation_id\":\"03f05b91-f803-45c9-b4bf-6a5f8ddf4a2c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":612180,\"team_id\":2,\"email\":\"adelina.petrova@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"03f05b91-f803-45c9-b4bf-6a5f8ddf4a2c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":612180,\"participants_processed\":2,\"exact_matches\":1,\"domain_matches\":0,\"best_match_found\":true} {\"correlation_id\":\"03f05b91-f803-45c9-b4bf-6a5f8ddf4a2c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612180} {\"correlation_id\":\"03f05b91-f803-45c9-b4bf-6a5f8ddf4a2c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612180,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"03f05b91-f803-45c9-b4bf-6a5f8ddf4a2c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":612180,\"remote_search\":true,\"lead_id\":null,\"contact_id\":4487,\"account_id\":244,\"opportunity_id\":299,\"stage_id\":36} {\"correlation_id\":\"03f05b91-f803-45c9-b4bf-6a5f8ddf4a2c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":611455,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89}} {\"correlation_id\":\"299c67e4-7026-46ee-b10d-d4e48fb55a7c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":611455} {\"correlation_id\":\"299c67e4-7026-46ee-b10d-d4e48fb55a7c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":611455,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"299c67e4-7026-46ee-b10d-d4e48fb55a7c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":611455,\"participants\":[{\"id\":997961,\"user_id\":null,\"contact_id\":null,\"lead_id\":null},{\"id\":997962,\"user_id\":1460,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"299c67e4-7026-46ee-b10d-d4e48fb55a7c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"299c67e4-7026-46ee-b10d-d4e48fb55a7c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"299c67e4-7026-46ee-b10d-d4e48fb55a7c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"299c67e4-7026-46ee-b10d-d4e48fb55a7c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"299c67e4-7026-46ee-b10d-d4e48fb55a7c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [Prospect match] Cache miss, calling the API {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"299c67e4-7026-46ee-b10d-d4e48fb55a7c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [Hubspot] Failed to fetch contact {\"email\":\"support@staging.jiminny.com\",\"reason\":\"[404] Client error: `GET https://api.hubapi.com/crm/v3/objects/contacts/support%40staging.jiminny.com?properties=email%2Cfirstname%2Clastname%2Ccountry%2Cphone%2Cmobilephone%2Cjobtitle%2Chubspot_owner_id%2Cassociatedcompanyid%2Cphoto&archived=0&idProperty=email` resulted in a `404 Not Found` response\"} {\"correlation_id\":\"299c67e4-7026-46ee-b10d-d4e48fb55a7c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [Prospect match] API returned empty result, caching the miss with empty prospect data {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"299c67e4-7026-46ee-b10d-d4e48fb55a7c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [Hubspot] Pagination completed {\"team_id\":2,\"endpoint\":\"https://api.hubapi.com/crm/v3/objects/contact/search\",\"total_requests\":1,\"total_records_fetched\":0,\"total_elapsed_seconds\":0.24,\"average_seconds_per_request\":0.24} {\"correlation_id\":\"299c67e4-7026-46ee-b10d-d4e48fb55a7c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [Prospect match] Resolved company domain from email {\"email\":\"support@staging.jiminny.com\",\"domain\":\"jiminny.com\"} {\"correlation_id\":\"299c67e4-7026-46ee-b10d-d4e48fb55a7c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [Prospect match] Cache miss {\"identifier_type\":\"domain\",\"identifier\":\"jiminny.com\",\"crm\":\"hubspot\"} {\"correlation_id\":\"299c67e4-7026-46ee-b10d-d4e48fb55a7c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [Prospect match] Cache miss, calling the API {\"identifier_type\":\"domain\",\"identifier\":\"jiminny.com\"} {\"correlation_id\":\"299c67e4-7026-46ee-b10d-d4e48fb55a7c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:19] local.INFO: [HubSpot] importAccount {\"crm_provider_id\":\"749766179\",\"config_id\":2} {\"correlation_id\":\"299c67e4-7026-46ee-b10d-d4e48fb55a7c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:19] local.INFO: [HubSpot] CRM Search requested {\"request\":{\"filterGroups\":[{\"filters\":[{\"propertyName\":\"associations.company\",\"operator\":\"EQ\",\"value\":\"749766179\"},{\"propertyName\":\"dealstage\",\"operator\":\"NOT_IN\",\"values\":[\"closedwon\",\"4040964\",\"59247967\"]},{\"propertyName\":\"dealstage\",\"operator\":\"NOT_IN\",\"values\":[\"closedlost\",\"4040965\",\"59247968\"]}]}],\"sorts\":[{\"propertyName\":\"modifieddate\",\"direction\":\"DESCENDING\"}],\"properties\":[\"dealname\",\"amount\",\"hubspot_owner_id\",\"pipeline\",\"dealstage\",\"closedate\",\"deal_currency_code\"],\"limit\":200}} {\"correlation_id\":\"299c67e4-7026-46ee-b10d-d4e48fb55a7c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:19] local.INFO: [Hubspot] Pagination completed {\"team_id\":2,\"endpoint\":\"https://api.hubapi.com/crm/v3/objects/deals/search\",\"total_requests\":1,\"total_records_fetched\":10,\"total_elapsed_seconds\":0.34,\"average_seconds_per_request\":0.34} {\"correlation_id\":\"299c67e4-7026-46ee-b10d-d4e48fb55a7c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:20] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":611455,\"team_id\":2,\"email\":\"aneliya.angelova@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"299c67e4-7026-46ee-b10d-d4e48fb55a7c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:21] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":611455,\"participants_processed\":2,\"exact_matches\":0,\"domain_matches\":1,\"best_match_found\":true} {\"correlation_id\":\"299c67e4-7026-46ee-b10d-d4e48fb55a7c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:21] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":611455} {\"correlation_id\":\"299c67e4-7026-46ee-b10d-d4e48fb55a7c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:21] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":611455,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"299c67e4-7026-46ee-b10d-d4e48fb55a7c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:21] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":611455,\"remote_search\":true,\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89} {\"correlation_id\":\"299c67e4-7026-46ee-b10d-d4e48fb55a7c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:21] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":611451,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89}} {\"correlation_id\":\"2385adb3-c893-49f8-9fbc-65b354009bea\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:21] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":611451} {\"correlation_id\":\"2385adb3-c893-49f8-9fbc-65b354009bea\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:21] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":611451,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"2385adb3-c893-49f8-9fbc-65b354009bea\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:21] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":611451,\"participants\":[{\"id\":997955,\"user_id\":null,\"contact_id\":null,\"lead_id\":null},{\"id\":997956,\"user_id\":18,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"2385adb3-c893-49f8-9fbc-65b354009bea\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:21] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"2385adb3-c893-49f8-9fbc-65b354009bea\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:21] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"2385adb3-c893-49f8-9fbc-65b354009bea\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:21] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"2385adb3-c893-49f8-9fbc-65b354009bea\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:21] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"2385adb3-c893-49f8-9fbc-65b354009bea\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:21] local.INFO: [Prospect match] Cache miss, calling the API {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"2385adb3-c893-49f8-9fbc-65b354009bea\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:23] local.INFO: [Hubspot] Failed to fetch contact {\"email\":\"support@staging.jiminny.com\",\"reason\":\"[404] Client error: `GET https://api.hubapi.com/crm/v3/objects/contacts/support%40staging.jiminny.com?properties=email%2Cfirstname%2Clastname%2Ccountry%2Cphone%2Cmobilephone%2Cjobtitle%2Chubspot_owner_id%2Cassociatedcompanyid%2Cphoto&archived=0&idProperty=email` resulted in a `404 Not Found` response\"} {\"correlation_id\":\"2385adb3-c893-49f8-9fbc-65b354009bea\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:23] local.INFO: [Prospect match] API returned empty result, caching the miss with empty prospect data {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"2385adb3-c893-49f8-9fbc-65b354009bea\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:23] local.INFO: [Hubspot] Pagination completed {\"team_id\":2,\"endpoint\":\"https://api.hubapi.com/crm/v3/objects/contact/search\",\"total_requests\":1,\"total_records_fetched\":0,\"total_elapsed_seconds\":0.49,\"average_seconds_per_request\":0.49} {\"correlation_id\":\"2385adb3-c893-49f8-9fbc-65b354009bea\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:24] local.INFO: [Prospect match] Resolved company domain from email {\"email\":\"support@staging.jiminny.com\",\"domain\":\"jiminny.com\"} {\"correlation_id\":\"2385adb3-c893-49f8-9fbc-65b354009bea\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:24] local.INFO: [Prospect match] Cache miss {\"identifier_type\":\"domain\",\"identifier\":\"jiminny.com\",\"crm\":\"hubspot\"} {\"correlation_id\":\"2385adb3-c893-49f8-9fbc-65b354009bea\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:24] local.INFO: [Prospect match] Cache miss, calling the API {\"identifier_type\":\"domain\",\"identifier\":\"jiminny.com\"} {\"correlation_id\":\"2385adb3-c893-49f8-9fbc-65b354009bea\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:24] local.INFO: [HubSpot] importAccount {\"crm_provider_id\":\"749766179\",\"config_id\":2} {\"correlation_id\":\"2385adb3-c893-49f8-9fbc-65b354009bea\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:24] local.INFO: [HubSpot] CRM Search requested {\"request\":{\"filterGroups\":[{\"filters\":[{\"propertyName\":\"associations.company\",\"operator\":\"EQ\",\"value\":\"749766179\"},{\"propertyName\":\"dealstage\",\"operator\":\"NOT_IN\",\"values\":[\"closedwon\",\"4040964\",\"59247967\"]},{\"propertyName\":\"dealstage\",\"operator\":\"NOT_IN\",\"values\":[\"closedlost\",\"4040965\",\"59247968\"]}]}],\"sorts\":[{\"propertyName\":\"modifieddate\",\"direction\":\"DESCENDING\"}],\"properties\":[\"dealname\",\"amount\",\"hubspot_owner_id\",\"pipeline\",\"dealstage\",\"closedate\",\"deal_currency_code\"],\"limit\":200}} {\"correlation_id\":\"2385adb3-c893-49f8-9fbc-65b354009bea\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:24] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"activity:purge-stale\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.727} {\"correlation_id\":\"1b5b216e-e0a9-4898-9f26-180cd26a0438\",\"trace_id\":\"f8d4ad10-fc79-469f-a5b8-e89d8fe8c392\"}\n[2026-05-11 11:06:24] local.INFO: [ EsUpdateProcessManager ] Finished updating entities in ES {\"worker\":\"\",\"peak_memory\":\"99.73 MB\",\"elapsed_seconds\":3.18,\"update_target\":\"activities\",\"should_iterate_again\":false} {\"correlation_id\":\"38935ae1-fcb3-437e-9c9e-1b7415a0b7e1\",\"trace_id\":\"325a2f9b-9c66-4223-ac02-1a43723f4315\"}\n[2026-05-11 11:06:24] local.INFO: [Hubspot] Pagination completed {\"team_id\":2,\"endpoint\":\"https://api.hubapi.com/crm/v3/objects/deals/search\",\"total_requests\":1,\"total_records_fetched\":10,\"total_elapsed_seconds\":0.26,\"average_seconds_per_request\":0.26} {\"correlation_id\":\"2385adb3-c893-49f8-9fbc-65b354009bea\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:25] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage for command {\"command\":\"activity:purge-stale\",\"memoryBeforeCommandInMb\":60.0,\"memoryAfterCommandInMB\":60.0,\"memoryPeakBeforeCommandInMb\":99.727,\"memoryPeakAfterCommandInMB\":99.727} {\"correlation_id\":\"1b5b216e-e0a9-4898-9f26-180cd26a0438\",\"trace_id\":\"f8d4ad10-fc79-469f-a5b8-e89d8fe8c392\"}\n[2026-05-11 11:06:25] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":611451,\"team_id\":2,\"email\":\"veselin.kulov@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"2385adb3-c893-49f8-9fbc-65b354009bea\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:25] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":611451,\"participants_processed\":2,\"exact_matches\":0,\"domain_matches\":1,\"best_match_found\":true} {\"correlation_id\":\"2385adb3-c893-49f8-9fbc-65b354009bea\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:25] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":611451} {\"correlation_id\":\"2385adb3-c893-49f8-9fbc-65b354009bea\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:25] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":611451,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"2385adb3-c893-49f8-9fbc-65b354009bea\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:25] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":611451,\"remote_search\":true,\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89} {\"correlation_id\":\"2385adb3-c893-49f8-9fbc-65b354009bea\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":611087,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89}} {\"correlation_id\":\"a6d1c39e-8bb7-43da-81ba-7a22dc8de463\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":611087} {\"correlation_id\":\"a6d1c39e-8bb7-43da-81ba-7a22dc8de463\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":611087,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"a6d1c39e-8bb7-43da-81ba-7a22dc8de463\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":611087,\"participants\":[{\"id\":997368,\"user_id\":null,\"contact_id\":null,\"lead_id\":null},{\"id\":997369,\"user_id\":18,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"a6d1c39e-8bb7-43da-81ba-7a22dc8de463\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"a6d1c39e-8bb7-43da-81ba-7a22dc8de463\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"a6d1c39e-8bb7-43da-81ba-7a22dc8de463\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"a6d1c39e-8bb7-43da-81ba-7a22dc8de463\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"a6d1c39e-8bb7-43da-81ba-7a22dc8de463\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"a6d1c39e-8bb7-43da-81ba-7a22dc8de463\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [Prospect match] cached empty result - no API calls, try next matching method {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"a6d1c39e-8bb7-43da-81ba-7a22dc8de463\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [Prospect match] Resolved company domain from email {\"email\":\"support@staging.jiminny.com\",\"domain\":\"jiminny.com\"} {\"correlation_id\":\"a6d1c39e-8bb7-43da-81ba-7a22dc8de463\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":611087,\"team_id\":2,\"email\":\"veselin.kulov@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"a6d1c39e-8bb7-43da-81ba-7a22dc8de463\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":611087,\"participants_processed\":2,\"exact_matches\":0,\"domain_matches\":1,\"best_match_found\":true} {\"correlation_id\":\"a6d1c39e-8bb7-43da-81ba-7a22dc8de463\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":611087} {\"correlation_id\":\"a6d1c39e-8bb7-43da-81ba-7a22dc8de463\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":611087,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"a6d1c39e-8bb7-43da-81ba-7a22dc8de463\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":611087,\"remote_search\":true,\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89} {\"correlation_id\":\"a6d1c39e-8bb7-43da-81ba-7a22dc8de463\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":611076,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89}} {\"correlation_id\":\"8905b485-a3d2-474a-a2dd-dd5375deb178\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":611076} {\"correlation_id\":\"8905b485-a3d2-474a-a2dd-dd5375deb178\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":611076,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"8905b485-a3d2-474a-a2dd-dd5375deb178\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":611076,\"participants\":[{\"id\":997346,\"user_id\":null,\"contact_id\":null,\"lead_id\":null},{\"id\":997347,\"user_id\":18,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"8905b485-a3d2-474a-a2dd-dd5375deb178\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"8905b485-a3d2-474a-a2dd-dd5375deb178\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"8905b485-a3d2-474a-a2dd-dd5375deb178\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"8905b485-a3d2-474a-a2dd-dd5375deb178\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"8905b485-a3d2-474a-a2dd-dd5375deb178\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"8905b485-a3d2-474a-a2dd-dd5375deb178\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [Prospect match] cached empty result - no API calls, try next matching method {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"8905b485-a3d2-474a-a2dd-dd5375deb178\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [Prospect match] Resolved company domain from email {\"email\":\"support@staging.jiminny.com\",\"domain\":\"jiminny.com\"} {\"correlation_id\":\"8905b485-a3d2-474a-a2dd-dd5375deb178\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":611076,\"team_id\":2,\"email\":\"veselin.kulov@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"8905b485-a3d2-474a-a2dd-dd5375deb178\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":611076,\"participants_processed\":2,\"exact_matches\":0,\"domain_matches\":1,\"best_match_found\":true} {\"correlation_id\":\"8905b485-a3d2-474a-a2dd-dd5375deb178\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":611076} {\"correlation_id\":\"8905b485-a3d2-474a-a2dd-dd5375deb178\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":611076,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"8905b485-a3d2-474a-a2dd-dd5375deb178\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":611076,\"remote_search\":true,\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89} {\"correlation_id\":\"8905b485-a3d2-474a-a2dd-dd5375deb178\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":610935,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89}} {\"correlation_id\":\"77fcbcd0-06e6-4680-85f5-cdefdd76007c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610935} {\"correlation_id\":\"77fcbcd0-06e6-4680-85f5-cdefdd76007c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610935,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"77fcbcd0-06e6-4680-85f5-cdefdd76007c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":610935,\"participants\":[{\"id\":997141,\"user_id\":null,\"contact_id\":null,\"lead_id\":null},{\"id\":997142,\"user_id\":18,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"77fcbcd0-06e6-4680-85f5-cdefdd76007c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"77fcbcd0-06e6-4680-85f5-cdefdd76007c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"77fcbcd0-06e6-4680-85f5-cdefdd76007c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"77fcbcd0-06e6-4680-85f5-cdefdd76007c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"77fcbcd0-06e6-4680-85f5-cdefdd76007c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"77fcbcd0-06e6-4680-85f5-cdefdd76007c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [Prospect match] cached empty result - no API calls, try next matching method {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"77fcbcd0-06e6-4680-85f5-cdefdd76007c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [Prospect match] Resolved company domain from email {\"email\":\"support@staging.jiminny.com\",\"domain\":\"jiminny.com\"} {\"correlation_id\":\"77fcbcd0-06e6-4680-85f5-cdefdd76007c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":610935,\"team_id\":2,\"email\":\"veselin.kulov@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"77fcbcd0-06e6-4680-85f5-cdefdd76007c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":610935,\"participants_processed\":2,\"exact_matches\":0,\"domain_matches\":1,\"best_match_found\":true} {\"correlation_id\":\"77fcbcd0-06e6-4680-85f5-cdefdd76007c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610935} {\"correlation_id\":\"77fcbcd0-06e6-4680-85f5-cdefdd76007c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610935,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"77fcbcd0-06e6-4680-85f5-cdefdd76007c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":610935,\"remote_search\":true,\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89} {\"correlation_id\":\"77fcbcd0-06e6-4680-85f5-cdefdd76007c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":610915,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89}} {\"correlation_id\":\"79639904-266b-4873-8940-014db94b5d8f\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610915} {\"correlation_id\":\"79639904-266b-4873-8940-014db94b5d8f\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610915,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"79639904-266b-4873-8940-014db94b5d8f\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":610915,\"participants\":[{\"id\":997104,\"user_id\":null,\"contact_id\":null,\"lead_id\":null},{\"id\":997105,\"user_id\":18,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"79639904-266b-4873-8940-014db94b5d8f\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"79639904-266b-4873-8940-014db94b5d8f\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"79639904-266b-4873-8940-014db94b5d8f\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"79639904-266b-4873-8940-014db94b5d8f\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"79639904-266b-4873-8940-014db94b5d8f\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"79639904-266b-4873-8940-014db94b5d8f\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [Prospect match] cached empty result - no API calls, try next matching method {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"79639904-266b-4873-8940-014db94b5d8f\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [Prospect match] Resolved company domain from email {\"email\":\"support@staging.jiminny.com\",\"domain\":\"jiminny.com\"} {\"correlation_id\":\"79639904-266b-4873-8940-014db94b5d8f\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":610915,\"team_id\":2,\"email\":\"veselin.kulov@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"79639904-266b-4873-8940-014db94b5d8f\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":610915,\"participants_processed\":2,\"exact_matches\":0,\"domain_matches\":1,\"best_match_found\":true} {\"correlation_id\":\"79639904-266b-4873-8940-014db94b5d8f\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610915} {\"correlation_id\":\"79639904-266b-4873-8940-014db94b5d8f\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610915,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"79639904-266b-4873-8940-014db94b5d8f\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":610915,\"remote_search\":true,\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89} {\"correlation_id\":\"79639904-266b-4873-8940-014db94b5d8f\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":610900,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89}} {\"correlation_id\":\"068ab494-a35a-4562-b447-44bf2ddb775d\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610900} {\"correlation_id\":\"068ab494-a35a-4562-b447-44bf2ddb775d\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610900,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"068ab494-a35a-4562-b447-44bf2ddb775d\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":610900,\"participants\":[{\"id\":997081,\"user_id\":null,\"contact_id\":null,\"lead_id\":null},{\"id\":997082,\"user_id\":18,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"068ab494-a35a-4562-b447-44bf2ddb775d\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"068ab494-a35a-4562-b447-44bf2ddb775d\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"068ab494-a35a-4562-b447-44bf2ddb775d\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"068ab494-a35a-4562-b447-44bf2ddb775d\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"068ab494-a35a-4562-b447-44bf2ddb775d\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"068ab494-a35a-4562-b447-44bf2ddb775d\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [Prospect match] cached empty result - no API calls, try next matching method {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"068ab494-a35a-4562-b447-44bf2ddb775d\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [Prospect match] Resolved company domain from email {\"email\":\"support@staging.jiminny.com\",\"domain\":\"jiminny.com\"} {\"correlation_id\":\"068ab494-a35a-4562-b447-44bf2ddb775d\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":610900,\"team_id\":2,\"email\":\"veselin.kulov@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"068ab494-a35a-4562-b447-44bf2ddb775d\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":610900,\"participants_processed\":2,\"exact_matches\":0,\"domain_matches\":1,\"best_match_found\":true} {\"correlation_id\":\"068ab494-a35a-4562-b447-44bf2ddb775d\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610900} {\"correlation_id\":\"068ab494-a35a-4562-b447-44bf2ddb775d\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610900,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"068ab494-a35a-4562-b447-44bf2ddb775d\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":610900,\"remote_search\":true,\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89} {\"correlation_id\":\"068ab494-a35a-4562-b447-44bf2ddb775d\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":610885,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89}} {\"correlation_id\":\"a556c8c6-d98c-4c17-b45c-e5ba57a68062\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610885} {\"correlation_id\":\"a556c8c6-d98c-4c17-b45c-e5ba57a68062\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610885,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"a556c8c6-d98c-4c17-b45c-e5ba57a68062\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":610885,\"participants\":[{\"id\":997051,\"user_id\":null,\"contact_id\":null,\"lead_id\":null},{\"id\":997052,\"user_id\":18,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"a556c8c6-d98c-4c17-b45c-e5ba57a68062\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"a556c8c6-d98c-4c17-b45c-e5ba57a68062\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"a556c8c6-d98c-4c17-b45c-e5ba57a68062\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"a556c8c6-d98c-4c17-b45c-e5ba57a68062\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"a556c8c6-d98c-4c17-b45c-e5ba57a68062\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"a556c8c6-d98c-4c17-b45c-e5ba57a68062\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [Prospect match] cached empty result - no API calls, try next matching method {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"a556c8c6-d98c-4c17-b45c-e5ba57a68062\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [Prospect match] Resolved company domain from email {\"email\":\"support@staging.jiminny.com\",\"domain\":\"jiminny.com\"} {\"correlation_id\":\"a556c8c6-d98c-4c17-b45c-e5ba57a68062\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":610885,\"team_id\":2,\"email\":\"veselin.kulov@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"a556c8c6-d98c-4c17-b45c-e5ba57a68062\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":610885,\"participants_processed\":2,\"exact_matches\":0,\"domain_matches\":1,\"best_match_found\":true} {\"correlation_id\":\"a556c8c6-d98c-4c17-b45c-e5ba57a68062\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610885} {\"correlation_id\":\"a556c8c6-d98c-4c17-b45c-e5ba57a68062\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610885,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"a556c8c6-d98c-4c17-b45c-e5ba57a68062\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":610885,\"remote_search\":true,\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89} {\"correlation_id\":\"a556c8c6-d98c-4c17-b45c-e5ba57a68062\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":610878,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89}} {\"correlation_id\":\"915d4deb-56d3-4de7-bc74-2274b327505c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610878} {\"correlation_id\":\"915d4deb-56d3-4de7-bc74-2274b327505c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610878,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"915d4deb-56d3-4de7-bc74-2274b327505c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":610878,\"participants\":[{\"id\":997035,\"user_id\":null,\"contact_id\":null,\"lead_id\":null},{\"id\":997036,\"user_id\":18,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"915d4deb-56d3-4de7-bc74-2274b327505c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"915d4deb-56d3-4de7-bc74-2274b327505c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"915d4deb-56d3-4de7-bc74-2274b327505c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"915d4deb-56d3-4de7-bc74-2274b327505c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"915d4deb-56d3-4de7-bc74-2274b327505c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"915d4deb-56d3-4de7-bc74-2274b327505c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [Prospect match] cached empty result - no API calls, try next matching method {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"915d4deb-56d3-4de7-bc74-2274b327505c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [Prospect match] Resolved company domain from email {\"email\":\"support@staging.jiminny.com\",\"domain\":\"jiminny.com\"} {\"correlation_id\":\"915d4deb-56d3-4de7-bc74-2274b327505c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":610878,\"team_id\":2,\"email\":\"veselin.kulov@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"915d4deb-56d3-4de7-bc74-2274b327505c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":610878,\"participants_processed\":2,\"exact_matches\":0,\"domain_matches\":1,\"best_match_found\":true} {\"correlation_id\":\"915d4deb-56d3-4de7-bc74-2274b327505c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610878} {\"correlation_id\":\"915d4deb-56d3-4de7-bc74-2274b327505c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610878,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"915d4deb-56d3-4de7-bc74-2274b327505c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":610878,\"remote_search\":true,\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89} {\"correlation_id\":\"915d4deb-56d3-4de7-bc74-2274b327505c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":610874,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89}} {\"correlation_id\":\"1b89c98c-65d3-4286-b140-c9453bca9a74\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610874} {\"correlation_id\":\"1b89c98c-65d3-4286-b140-c9453bca9a74\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610874,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"1b89c98c-65d3-4286-b140-c9453bca9a74\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":610874,\"participants\":[{\"id\":997025,\"user_id\":null,\"contact_id\":null,\"lead_id\":null},{\"id\":997026,\"user_id\":18,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"1b89c98c-65d3-4286-b140-c9453bca9a74\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"1b89c98c-65d3-4286-b140-c9453bca9a74\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"1b89c98c-65d3-4286-b140-c9453bca9a74\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"1b89c98c-65d3-4286-b140-c9453bca9a74\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"1b89c98c-65d3-4286-b140-c9453bca9a74\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"1b89c98c-65d3-4286-b140-c9453bca9a74\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [Prospect match] cached empty result - no API calls, try next matching method {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"1b89c98c-65d3-4286-b140-c9453bca9a74\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [Prospect match] Resolved company domain from email {\"email\":\"support@staging.jiminny.com\",\"domain\":\"jiminny.com\"} {\"correlation_id\":\"1b89c98c-65d3-4286-b140-c9453bca9a74\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":610874,\"team_id\":2,\"email\":\"veselin.kulov@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"1b89c98c-65d3-4286-b140-c9453bca9a74\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":610874,\"participants_processed\":2,\"exact_matches\":0,\"domain_matches\":1,\"best_match_found\":true} {\"correlation_id\":\"1b89c98c-65d3-4286-b140-c9453bca9a74\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610874} {\"correlation_id\":\"1b89c98c-65d3-4286-b140-c9453bca9a74\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610874,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"1b89c98c-65d3-4286-b140-c9453bca9a74\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":610874,\"remote_search\":true,\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89} {\"correlation_id\":\"1b89c98c-65d3-4286-b140-c9453bca9a74\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":610867,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89}} {\"correlation_id\":\"8a33657f-373a-4b5d-b1f9-71bbe85bd3ba\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610867} {\"correlation_id\":\"8a33657f-373a-4b5d-b1f9-71bbe85bd3ba\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610867,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"8a33657f-373a-4b5d-b1f9-71bbe85bd3ba\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":610867,\"participants\":[{\"id\":997011,\"user_id\":null,\"contact_id\":null,\"lead_id\":null},{\"id\":997012,\"user_id\":18,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"8a33657f-373a-4b5d-b1f9-71bbe85bd3ba\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"8a33657f-373a-4b5d-b1f9-71bbe85bd3ba\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"8a33657f-373a-4b5d-b1f9-71bbe85bd3ba\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"8a33657f-373a-4b5d-b1f9-71bbe85bd3ba\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"8a33657f-373a-4b5d-b1f9-71bbe85bd3ba\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"8a33657f-373a-4b5d-b1f9-71bbe85bd3ba\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [Prospect match] cached empty result - no API calls, try next matching method {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"8a33657f-373a-4b5d-b1f9-71bbe85bd3ba\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [Prospect match] Resolved company domain from email {\"email\":\"support@staging.jiminny.com\",\"domain\":\"jiminny.com\"} {\"correlation_id\":\"8a33657f-373a-4b5d-b1f9-71bbe85bd3ba\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":610867,\"team_id\":2,\"email\":\"veselin.kulov@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"8a33657f-373a-4b5d-b1f9-71bbe85bd3ba\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":610867,\"participants_processed\":2,\"exact_matches\":0,\"domain_matches\":1,\"best_match_found\":true} {\"correlation_id\":\"8a33657f-373a-4b5d-b1f9-71bbe85bd3ba\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610867} {\"correlation_id\":\"8a33657f-373a-4b5d-b1f9-71bbe85bd3ba\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610867,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"8a33657f-373a-4b5d-b1f9-71bbe85bd3ba\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":610867,\"remote_search\":true,\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89} {\"correlation_id\":\"8a33657f-373a-4b5d-b1f9-71bbe85bd3ba\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":610764,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89}} {\"correlation_id\":\"440ce0fb-99c1-458c-9db4-c11e504dd620\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610764} {\"correlation_id\":\"440ce0fb-99c1-458c-9db4-c11e504dd620\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610764,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"440ce0fb-99c1-458c-9db4-c11e504dd620\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":610764,\"participants\":[{\"id\":996951,\"user_id\":null,\"contact_id\":null,\"lead_id\":null},{\"id\":996952,\"user_id\":18,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"440ce0fb-99c1-458c-9db4-c11e504dd620\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"440ce0fb-99c1-458c-9db4-c11e504dd620\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"440ce0fb-99c1-458c-9db4-c11e504dd620\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"440ce0fb-99c1-458c-9db4-c11e504dd620\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"440ce0fb-99c1-458c-9db4-c11e504dd620\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"440ce0fb-99c1-458c-9db4-c11e504dd620\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [Prospect match] cached empty result - no API calls, try next matching method {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"440ce0fb-99c1-458c-9db4-c11e504dd620\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [Prospect match] Resolved company domain from email {\"email\":\"support@staging.jiminny.com\",\"domain\":\"jiminny.com\"} {\"correlation_id\":\"440ce0fb-99c1-458c-9db4-c11e504dd620\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":610764,\"team_id\":2,\"email\":\"veselin.kulov@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"440ce0fb-99c1-458c-9db4-c11e504dd620\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":610764,\"participants_processed\":2,\"exact_matches\":0,\"domain_matches\":1,\"best_match_found\":true} {\"correlation_id\":\"440ce0fb-99c1-458c-9db4-c11e504dd620\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610764} {\"correlation_id\":\"440ce0fb-99c1-458c-9db4-c11e504dd620\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610764,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"440ce0fb-99c1-458c-9db4-c11e504dd620\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":610764,\"remote_search\":true,\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89} {\"correlation_id\":\"440ce0fb-99c1-458c-9db4-c11e504dd620\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":610617,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89}} {\"correlation_id\":\"d8853964-8fe9-4553-96da-d088b6dfc843\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610617} {\"correlation_id\":\"d8853964-8fe9-4553-96da-d088b6dfc843\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610617,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"d8853964-8fe9-4553-96da-d088b6dfc843\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":610617,\"participants\":[{\"id\":996641,\"user_id\":null,\"contact_id\":null,\"lead_id\":null},{\"id\":996642,\"user_id\":18,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"d8853964-8fe9-4553-96da-d088b6dfc843\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"d8853964-8fe9-4553-96da-d088b6dfc843\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"d8853964-8fe9-4553-96da-d088b6dfc843\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"d8853964-8fe9-4553-96da-d088b6dfc843\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"d8853964-8fe9-4553-96da-d088b6dfc843\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"d8853964-8fe9-4553-96da-d088b6dfc843\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [Prospect match] cached empty result - no API calls, try next matching method {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"d8853964-8fe9-4553-96da-d088b6dfc843\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [Prospect match] Resolved company domain from email {\"email\":\"support@staging.jiminny.com\",\"domain\":\"jiminny.com\"} {\"correlation_id\":\"d8853964-8fe9-4553-96da-d088b6dfc843\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":610617,\"team_id\":2,\"email\":\"veselin.kulov@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"d8853964-8fe9-4553-96da-d088b6dfc843\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":610617,\"participants_processed\":2,\"exact_matches\":0,\"domain_matches\":1,\"best_match_found\":true} {\"correlation_id\":\"d8853964-8fe9-4553-96da-d088b6dfc843\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610617} {\"correlation_id\":\"d8853964-8fe9-4553-96da-d088b6dfc843\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610617,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"d8853964-8fe9-4553-96da-d088b6dfc843\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":610617,\"remote_search\":true,\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89} {\"correlation_id\":\"d8853964-8fe9-4553-96da-d088b6dfc843\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":610539,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89}} {\"correlation_id\":\"d021767c-298a-4ad0-9f72-dca085cc6528\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610539} {\"correlation_id\":\"d021767c-298a-4ad0-9f72-dca085cc6528\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610539,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"d021767c-298a-4ad0-9f72-dca085cc6528\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":610539,\"participants\":[{\"id\":996485,\"user_id\":null,\"contact_id\":null,\"lead_id\":null},{\"id\":996486,\"user_id\":18,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"d021767c-298a-4ad0-9f72-dca085cc6528\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"d021767c-298a-4ad0-9f72-dca085cc6528\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"d021767c-298a-4ad0-9f72-dca085cc6528\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"d021767c-298a-4ad0-9f72-dca085cc6528\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"d021767c-298a-4ad0-9f72-dca085cc6528\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"d021767c-298a-4ad0-9f72-dca085cc6528\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [Prospect match] cached empty result - no API calls, try next matching method {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"d021767c-298a-4ad0-9f72-dca085cc6528\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [Prospect match] Resolved company domain from email {\"email\":\"support@staging.jiminny.com\",\"domain\":\"jiminny.com\"} {\"correlation_id\":\"d021767c-298a-4ad0-9f72-dca085cc6528\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":610539,\"team_id\":2,\"email\":\"veselin.kulov@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"d021767c-298a-4ad0-9f72-dca085cc6528\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":610539,\"participants_processed\":2,\"exact_matches\":0,\"domain_matches\":1,\"best_match_found\":true} {\"correlation_id\":\"d021767c-298a-4ad0-9f72-dca085cc6528\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610539} {\"correlation_id\":\"d021767c-298a-4ad0-9f72-dca085cc6528\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610539,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"d021767c-298a-4ad0-9f72-dca085cc6528\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"mailbox:text-relay:sync\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.727} {\"correlation_id\":\"7c0c4db8-2e4e-4aa1-aa71-a3d63cdb81d7\",\"trace_id\":\"b3e9e2f5-3256-429a-ad76-e46e3c2e626d\"}\n[2026-05-11 11:06:28] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":610539,\"remote_search\":true,\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89} {\"correlation_id\":\"d021767c-298a-4ad0-9f72-dca085cc6528\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":610528,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89}} {\"correlation_id\":\"2410a4b8-a997-4cf1-99f0-c23ba54805c7\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610528} {\"correlation_id\":\"2410a4b8-a997-4cf1-99f0-c23ba54805c7\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610528,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"2410a4b8-a997-4cf1-99f0-c23ba54805c7\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":610528,\"participants\":[{\"id\":996463,\"user_id\":null,\"contact_id\":null,\"lead_id\":null},{\"id\":996464,\"user_id\":18,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"2410a4b8-a997-4cf1-99f0-c23ba54805c7\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"2410a4b8-a997-4cf1-99f0-c23ba54805c7\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"2410a4b8-a997-4cf1-99f0-c23ba54805c7\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"2410a4b8-a997-4cf1-99f0-c23ba54805c7\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"2410a4b8-a997-4cf1-99f0-c23ba54805c7\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"2410a4b8-a997-4cf1-99f0-c23ba54805c7\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [Prospect match] cached empty result - no API calls, try next matching method {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"2410a4b8-a997-4cf1-99f0-c23ba54805c7\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [Prospect match] Resolved company domain from email {\"email\":\"support@staging.jiminny.com\",\"domain\":\"jiminny.com\"} {\"correlation_id\":\"2410a4b8-a997-4cf1-99f0-c23ba54805c7\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":610528,\"team_id\":2,\"email\":\"veselin.kulov@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"2410a4b8-a997-4cf1-99f0-c23ba54805c7\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":610528,\"participants_processed\":2,\"exact_matches\":0,\"domain_matches\":1,\"best_match_found\":true} {\"correlation_id\":\"2410a4b8-a997-4cf1-99f0-c23ba54805c7\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610528} {\"correlation_id\":\"2410a4b8-a997-4cf1-99f0-c23ba54805c7\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610528,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"2410a4b8-a997-4cf1-99f0-c23ba54805c7\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":610528,\"remote_search\":true,\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89} {\"correlation_id\":\"2410a4b8-a997-4cf1-99f0-c23ba54805c7\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":610506,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89}} {\"correlation_id\":\"8b5bce8c-7e89-46ba-b04b-9b9ab4e620b6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610506} {\"correlation_id\":\"8b5bce8c-7e89-46ba-b04b-9b9ab4e620b6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610506,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"8b5bce8c-7e89-46ba-b04b-9b9ab4e620b6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":610506,\"participants\":[{\"id\":996419,\"user_id\":null,\"contact_id\":null,\"lead_id\":null},{\"id\":996420,\"user_id\":18,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"8b5bce8c-7e89-46ba-b04b-9b9ab4e620b6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"8b5bce8c-7e89-46ba-b04b-9b9ab4e620b6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"8b5bce8c-7e89-46ba-b04b-9b9ab4e620b6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"8b5bce8c-7e89-46ba-b04b-9b9ab4e620b6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"8b5bce8c-7e89-46ba-b04b-9b9ab4e620b6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"8b5bce8c-7e89-46ba-b04b-9b9ab4e620b6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [Prospect match] cached empty result - no API calls, try next matching method {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"8b5bce8c-7e89-46ba-b04b-9b9ab4e620b6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [Prospect match] Resolved company domain from email {\"email\":\"support@staging.jiminny.com\",\"domain\":\"jiminny.com\"} {\"correlation_id\":\"8b5bce8c-7e89-46ba-b04b-9b9ab4e620b6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":610506,\"team_id\":2,\"email\":\"veselin.kulov@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"8b5bce8c-7e89-46ba-b04b-9b9ab4e620b6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":610506,\"participants_processed\":2,\"exact_matches\":0,\"domain_matches\":1,\"best_match_found\":true} {\"correlation_id\":\"8b5bce8c-7e89-46ba-b04b-9b9ab4e620b6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610506} {\"correlation_id\":\"8b5bce8c-7e89-46ba-b04b-9b9ab4e620b6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610506,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"8b5bce8c-7e89-46ba-b04b-9b9ab4e620b6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":610506,\"remote_search\":true,\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89} {\"correlation_id\":\"8b5bce8c-7e89-46ba-b04b-9b9ab4e620b6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":610497,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89}} {\"correlation_id\":\"a9e55e87-8951-406b-84a8-6fecf2841584\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610497} {\"correlation_id\":\"a9e55e87-8951-406b-84a8-6fecf2841584\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610497,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"a9e55e87-8951-406b-84a8-6fecf2841584\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":610497,\"participants\":[{\"id\":996401,\"user_id\":null,\"contact_id\":null,\"lead_id\":null},{\"id\":996402,\"user_id\":18,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"a9e55e87-8951-406b-84a8-6fecf2841584\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"a9e55e87-8951-406b-84a8-6fecf2841584\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"a9e55e87-8951-406b-84a8-6fecf2841584\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"a9e55e87-8951-406b-84a8-6fecf2841584\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"a9e55e87-8951-406b-84a8-6fecf2841584\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"a9e55e87-8951-406b-84a8-6fecf2841584\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [Prospect match] cached empty result - no API calls, try next matching method {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"a9e55e87-8951-406b-84a8-6fecf2841584\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [Prospect match] Resolved company domain from email {\"email\":\"support@staging.jiminny.com\",\"domain\":\"jiminny.com\"} {\"correlation_id\":\"a9e55e87-8951-406b-84a8-6fecf2841584\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":610497,\"team_id\":2,\"email\":\"veselin.kulov@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"a9e55e87-8951-406b-84a8-6fecf2841584\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":610497,\"participants_processed\":2,\"exact_matches\":0,\"domain_matches\":1,\"best_match_found\":true} {\"correlation_id\":\"a9e55e87-8951-406b-84a8-6fecf2841584\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610497} {\"correlation_id\":\"a9e55e87-8951-406b-84a8-6fecf2841584\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610497,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"a9e55e87-8951-406b-84a8-6fecf2841584\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":610497,\"remote_search\":true,\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89} {\"correlation_id\":\"a9e55e87-8951-406b-84a8-6fecf2841584\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":610490,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89}} {\"correlation_id\":\"209ccee3-3f37-4fb8-8648-ff2f554f6cf3\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610490} {\"correlation_id\":\"209ccee3-3f37-4fb8-8648-ff2f554f6cf3\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610490,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"209ccee3-3f37-4fb8-8648-ff2f554f6cf3\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":610490,\"participants\":[{\"id\":996385,\"user_id\":null,\"contact_id\":null,\"lead_id\":null},{\"id\":996386,\"user_id\":18,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"209ccee3-3f37-4fb8-8648-ff2f554f6cf3\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"209ccee3-3f37-4fb8-8648-ff2f554f6cf3\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"209ccee3-3f37-4fb8-8648-ff2f554f6cf3\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"209ccee3-3f37-4fb8-8648-ff2f554f6cf3\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"209ccee3-3f37-4fb8-8648-ff2f554f6cf3\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"209ccee3-3f37-4fb8-8648-ff2f554f6cf3\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [Prospect match] cached empty result - no API calls, try next matching method {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"209ccee3-3f37-4fb8-8648-ff2f554f6cf3\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [Prospect match] Resolved company domain from email {\"email\":\"support@staging.jiminny.com\",\"domain\":\"jiminny.com\"} {\"correlation_id\":\"209ccee3-3f37-4fb8-8648-ff2f554f6cf3\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":610490,\"team_id\":2,\"email\":\"veselin.kulov@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"209ccee3-3f37-4fb8-8648-ff2f554f6cf3\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":610490,\"participants_processed\":2,\"exact_matches\":0,\"domain_matches\":1,\"best_match_found\":true} {\"correlation_id\":\"209ccee3-3f37-4fb8-8648-ff2f554f6cf3\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610490} {\"correlation_id\":\"209ccee3-3f37-4fb8-8648-ff2f554f6cf3\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610490,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"209ccee3-3f37-4fb8-8648-ff2f554f6cf3\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":610490,\"remote_search\":true,\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89} {\"correlation_id\":\"209ccee3-3f37-4fb8-8648-ff2f554f6cf3\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":610470,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89}} {\"correlation_id\":\"0b6c4d04-c34f-4cfd-b391-aa0128a17792\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610470} {\"correlation_id\":\"0b6c4d04-c34f-4cfd-b391-aa0128a17792\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610470,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"0b6c4d04-c34f-4cfd-b391-aa0128a17792\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":610470,\"participants\":[{\"id\":996369,\"user_id\":null,\"contact_id\":null,\"lead_id\":null},{\"id\":996370,\"user_id\":18,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"0b6c4d04-c34f-4cfd-b391-aa0128a17792\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage for command {\"command\":\"mailbox:text-relay:sync\",\"memoryBeforeCommandInMb\":60.0,\"memoryAfterCommandInMB\":60.0,\"memoryPeakBeforeCommandInMb\":99.727,\"memoryPeakAfterCommandInMB\":99.727} {\"correlation_id\":\"7c0c4db8-2e4e-4aa1-aa71-a3d63cdb81d7\",\"trace_id\":\"b3e9e2f5-3256-429a-ad76-e46e3c2e626d\"}\n[2026-05-11 11:06:28] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"0b6c4d04-c34f-4cfd-b391-aa0128a17792\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"0b6c4d04-c34f-4cfd-b391-aa0128a17792\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"0b6c4d04-c34f-4cfd-b391-aa0128a17792\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"0b6c4d04-c34f-4cfd-b391-aa0128a17792\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"0b6c4d04-c34f-4cfd-b391-aa0128a17792\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [Prospect match] cached empty result - no API calls, try next matching method {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"0b6c4d04-c34f-4cfd-b391-aa0128a17792\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [Prospect match] Resolved company domain from email {\"email\":\"support@staging.jiminny.com\",\"domain\":\"jiminny.com\"} {\"correlation_id\":\"0b6c4d04-c34f-4cfd-b391-aa0128a17792\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":610470,\"team_id\":2,\"email\":\"veselin.kulov@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"0b6c4d04-c34f-4cfd-b391-aa0128a17792\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":610470,\"participants_processed\":2,\"exact_matches\":0,\"domain_matches\":1,\"best_match_found\":true} {\"correlation_id\":\"0b6c4d04-c34f-4cfd-b391-aa0128a17792\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610470} {\"correlation_id\":\"0b6c4d04-c34f-4cfd-b391-aa0128a17792\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610470,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"0b6c4d04-c34f-4cfd-b391-aa0128a17792\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":610470,\"remote_search\":true,\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89} {\"correlation_id\":\"0b6c4d04-c34f-4cfd-b391-aa0128a17792\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":610462,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89}} {\"correlation_id\":\"0584d557-927b-47a5-b193-04786c5c20bc\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610462} {\"correlation_id\":\"0584d557-927b-47a5-b193-04786c5c20bc\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610462,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"0584d557-927b-47a5-b193-04786c5c20bc\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":610462,\"participants\":[{\"id\":996353,\"user_id\":null,\"contact_id\":null,\"lead_id\":null},{\"id\":996354,\"user_id\":18,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"0584d557-927b-47a5-b193-04786c5c20bc\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"0584d557-927b-47a5-b193-04786c5c20bc\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"0584d557-927b-47a5-b193-04786c5c20bc\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"0584d557-927b-47a5-b193-04786c5c20bc\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"0584d557-927b-47a5-b193-04786c5c20bc\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"0584d557-927b-47a5-b193-04786c5c20bc\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [Prospect match] cached empty result - no API calls, try next matching method {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"0584d557-927b-47a5-b193-04786c5c20bc\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [Prospect match] Resolved company domain from email {\"email\":\"support@staging.jiminny.com\",\"domain\":\"jiminny.com\"} {\"correlation_id\":\"0584d557-927b-47a5-b193-04786c5c20bc\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":610462,\"team_id\":2,\"email\":\"veselin.kulov@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"0584d557-927b-47a5-b193-04786c5c20bc\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":610462,\"participants_processed\":2,\"exact_matches\":0,\"domain_matches\":1,\"best_match_found\":true} {\"correlation_id\":\"0584d557-927b-47a5-b193-04786c5c20bc\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610462} {\"correlation_id\":\"0584d557-927b-47a5-b193-04786c5c20bc\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610462,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"0584d557-927b-47a5-b193-04786c5c20bc\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":610462,\"remote_search\":true,\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89} {\"correlation_id\":\"0584d557-927b-47a5-b193-04786c5c20bc\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":610451,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89}} {\"correlation_id\":\"e86be547-45b5-48da-b389-b64c7928f856\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610451} {\"correlation_id\":\"e86be547-45b5-48da-b389-b64c7928f856\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610451,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"e86be547-45b5-48da-b389-b64c7928f856\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":610451,\"participants\":[{\"id\":996340,\"user_id\":null,\"contact_id\":null,\"lead_id\":null},{\"id\":996341,\"user_id\":18,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"e86be547-45b5-48da-b389-b64c7928f856\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"e86be547-45b5-48da-b389-b64c7928f856\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"e86be547-45b5-48da-b389-b64c7928f856\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"e86be547-45b5-48da-b389-b64c7928f856\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"e86be547-45b5-48da-b389-b64c7928f856\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"e86be547-45b5-48da-b389-b64c7928f856\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [Prospect match] cached empty result - no API calls, try next matching method {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"e86be547-45b5-48da-b389-b64c7928f856\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [Prospect match] Resolved company domain from email {\"email\":\"support@staging.jiminny.com\",\"domain\":\"jiminny.com\"} {\"correlation_id\":\"e86be547-45b5-48da-b389-b64c7928f856\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":610451,\"team_id\":2,\"email\":\"veselin.kulov@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"e86be547-45b5-48da-b389-b64c7928f856\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":610451,\"participants_processed\":2,\"exact_matches\":0,\"domain_matches\":1,\"best_match_found\":true} {\"correlation_id\":\"e86be547-45b5-48da-b389-b64c7928f856\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610451} {\"correlation_id\":\"e86be547-45b5-48da-b389-b64c7928f856\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610451,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"e86be547-45b5-48da-b389-b64c7928f856\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":610451,\"remote_search\":true,\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89} {\"correlation_id\":\"e86be547-45b5-48da-b389-b64c7928f856\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":610438,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89}} {\"correlation_id\":\"34e69f21-acca-487a-8565-fc42df81c0fe\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610438} {\"correlation_id\":\"34e69f21-acca-487a-8565-fc42df81c0fe\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610438,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"34e69f21-acca-487a-8565-fc42df81c0fe\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":610438,\"participants\":[{\"id\":996320,\"user_id\":null,\"contact_id\":null,\"lead_id\":null},{\"id\":996321,\"user_id\":18,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"34e69f21-acca-487a-8565-fc42df81c0fe\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"34e69f21-acca-487a-8565-fc42df81c0fe\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"34e69f21-acca-487a-8565-fc42df81c0fe\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"34e69f21-acca-487a-8565-fc42df81c0fe\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"34e69f21-acca-487a-8565-fc42df81c0fe\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"34e69f21-acca-487a-8565-fc42df81c0fe\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [Prospect match] cached empty result - no API calls, try next matching method {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"34e69f21-acca-487a-8565-fc42df81c0fe\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [Prospect match] Resolved company domain from email {\"email\":\"support@staging.jiminny.com\",\"domain\":\"jiminny.com\"} {\"correlation_id\":\"34e69f21-acca-487a-8565-fc42df81c0fe\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":610438,\"team_id\":2,\"email\":\"veselin.kulov@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"34e69f21-acca-487a-8565-fc42df81c0fe\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":610438,\"participants_processed\":2,\"exact_matches\":0,\"domain_matches\":1,\"best_match_found\":true} {\"correlation_id\":\"34e69f21-acca-487a-8565-fc42df81c0fe\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610438} {\"correlation_id\":\"34e69f21-acca-487a-8565-fc42df81c0fe\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610438,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"34e69f21-acca-487a-8565-fc42df81c0fe\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":610438,\"remote_search\":true,\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89} {\"correlation_id\":\"34e69f21-acca-487a-8565-fc42df81c0fe\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":610426,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89}} {\"correlation_id\":\"813c1b3a-95b1-4314-b468-5f42e908f99e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610426} {\"correlation_id\":\"813c1b3a-95b1-4314-b468-5f42e908f99e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610426,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"813c1b3a-95b1-4314-b468-5f42e908f99e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":610426,\"participants\":[{\"id\":996306,\"user_id\":null,\"contact_id\":null,\"lead_id\":null},{\"id\":996307,\"user_id\":18,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"813c1b3a-95b1-4314-b468-5f42e908f99e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"813c1b3a-95b1-4314-b468-5f42e908f99e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"813c1b3a-95b1-4314-b468-5f42e908f99e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"813c1b3a-95b1-4314-b468-5f42e908f99e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"813c1b3a-95b1-4314-b468-5f42e908f99e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"813c1b3a-95b1-4314-b468-5f42e908f99e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [Prospect match] cached empty result - no API calls, try next matching method {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"813c1b3a-95b1-4314-b468-5f42e908f99e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [Prospect match] Resolved company domain from email {\"email\":\"support@staging.jiminny.com\",\"domain\":\"jiminny.com\"} {\"correlation_id\":\"813c1b3a-95b1-4314-b468-5f42e908f99e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":610426,\"team_id\":2,\"email\":\"veselin.kulov@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"813c1b3a-95b1-4314-b468-5f42e908f99e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":610426,\"participants_processed\":2,\"exact_matches\":0,\"domain_matches\":1,\"best_match_found\":true} {\"correlation_id\":\"813c1b3a-95b1-4314-b468-5f42e908f99e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610426} {\"correlation_id\":\"813c1b3a-95b1-4314-b468-5f42e908f99e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610426,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"813c1b3a-95b1-4314-b468-5f42e908f99e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":610426,\"remote_search\":true,\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89} {\"correlation_id\":\"813c1b3a-95b1-4314-b468-5f42e908f99e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":610403,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89}} {\"correlation_id\":\"98026431-b755-4e7e-af17-86d8e2993031\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610403} {\"correlation_id\":\"98026431-b755-4e7e-af17-86d8e2993031\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610403,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"98026431-b755-4e7e-af17-86d8e2993031\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":610403,\"participants\":[{\"id\":996282,\"user_id\":null,\"contact_id\":null,\"lead_id\":null},{\"id\":996283,\"user_id\":18,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"98026431-b755-4e7e-af17-86d8e2993031\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"98026431-b755-4e7e-af17-86d8e2993031\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"98026431-b755-4e7e-af17-86d8e2993031\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"98026431-b755-4e7e-af17-86d8e2993031\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"98026431-b755-4e7e-af17-86d8e2993031\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"98026431-b755-4e7e-af17-86d8e2993031\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [Prospect match] cached empty result - no API calls, try next matching method {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"98026431-b755-4e7e-af17-86d8e2993031\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [Prospect match] Resolved company domain from email {\"email\":\"support@staging.jiminny.com\",\"domain\":\"jiminny.com\"} {\"correlation_id\":\"98026431-b755-4e7e-af17-86d8e2993031\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":610403,\"team_id\":2,\"email\":\"veselin.kulov@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"98026431-b755-4e7e-af17-86d8e2993031\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":610403,\"participants_processed\":2,\"exact_matches\":0,\"domain_matches\":1,\"best_match_found\":true} {\"correlation_id\":\"98026431-b755-4e7e-af17-86d8e2993031\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610403} {\"correlation_id\":\"98026431-b755-4e7e-af17-86d8e2993031\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610403,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"98026431-b755-4e7e-af17-86d8e2993031\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":610403,\"remote_search\":true,\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89} {\"correlation_id\":\"98026431-b755-4e7e-af17-86d8e2993031\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":610400,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":4487,\"account_id\":244,\"opportunity_id\":350,\"stage_id\":34}} {\"correlation_id\":\"4c991921-b335-438b-a656-aac035483aa5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610400} {\"correlation_id\":\"4c991921-b335-438b-a656-aac035483aa5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610400,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"4c991921-b335-438b-a656-aac035483aa5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":610400,\"participants\":[{\"id\":996275,\"user_id\":1460,\"contact_id\":null,\"lead_id\":null},{\"id\":996276,\"user_id\":206,\"contact_id\":null,\"lead_id\":null},{\"id\":996277,\"user_id\":null,\"contact_id\":4487,\"lead_id\":null}]} {\"correlation_id\":\"4c991921-b335-438b-a656-aac035483aa5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"4c991921-b335-438b-a656-aac035483aa5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"4c991921-b335-438b-a656-aac035483aa5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"4c991921-b335-438b-a656-aac035483aa5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"4c991921-b335-438b-a656-aac035483aa5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":610400,\"team_id\":2,\"email\":\"aneliya.angelova@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"4c991921-b335-438b-a656-aac035483aa5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":610400,\"team_id\":2,\"email\":\"horen.kirazyan@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"4c991921-b335-438b-a656-aac035483aa5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: ProspectCache - Searching DB for opportunity by owner {\"account_id\":244,\"contact_id\":4487,\"owner_id\":1460} {\"correlation_id\":\"4c991921-b335-438b-a656-aac035483aa5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: ProspectCache - Fallback DB opportunity search {\"account_id\":244,\"contact_id\":4487} {\"correlation_id\":\"4c991921-b335-438b-a656-aac035483aa5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: ProspectCache - Opportunity DB search results {\"account_id\":244,\"contact_id\":4487,\"opportunity_id\":350} {\"correlation_id\":\"4c991921-b335-438b-a656-aac035483aa5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"robinson@crusoe.com\"} {\"correlation_id\":\"4c991921-b335-438b-a656-aac035483aa5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":610400,\"participants_processed\":3,\"exact_matches\":1,\"domain_matches\":0,\"best_match_found\":true} {\"correlation_id\":\"4c991921-b335-438b-a656-aac035483aa5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610400} {\"correlation_id\":\"4c991921-b335-438b-a656-aac035483aa5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610400,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"4c991921-b335-438b-a656-aac035483aa5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":610400,\"remote_search\":true,\"lead_id\":null,\"contact_id\":4487,\"account_id\":244,\"opportunity_id\":350,\"stage_id\":34} {\"correlation_id\":\"4c991921-b335-438b-a656-aac035483aa5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [ EsUpdateProcessManager ] Finished updating entities in ES {\"worker\":\"\",\"peak_memory\":\"99.73 MB\",\"elapsed_seconds\":0.14,\"update_target\":\"activities\",\"should_iterate_again\":false} {\"correlation_id\":\"38935ae1-fcb3-437e-9c9e-1b7415a0b7e1\",\"trace_id\":\"325a2f9b-9c66-4223-ac02-1a43723f4315\"}\n[2026-05-11 11:06:30] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"conference:pre-meeting-notification\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.727} {\"correlation_id\":\"4fa18424-38b1-4f8a-a2e1-b467db754dba\",\"trace_id\":\"0d2c5c78-f780-4c85-81ff-c26ad669374b\"}\n[2026-05-11 11:06:30] local.INFO: Running pre-meeting notification command {\"correlation_id\":\"4fa18424-38b1-4f8a-a2e1-b467db754dba\",\"trace_id\":\"0d2c5c78-f780-4c85-81ff-c26ad669374b\"}\n[2026-05-11 11:06:30] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage for command {\"command\":\"conference:pre-meeting-notification\",\"memoryBeforeCommandInMb\":60.0,\"memoryAfterCommandInMB\":60.0,\"memoryPeakBeforeCommandInMb\":99.727,\"memoryPeakAfterCommandInMB\":99.727} {\"correlation_id\":\"4fa18424-38b1-4f8a-a2e1-b467db754dba\",\"trace_id\":\"0d2c5c78-f780-4c85-81ff-c26ad669374b\"}\n[2026-05-11 11:06:31] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"conference:monitor:start\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.727} {\"correlation_id\":\"ad10418d-4623-4ff3-b748-2e19330019b3\",\"trace_id\":\"a31c4682-7665-42af-99bd-8ab71b6cd9ac\"}\n[2026-05-11 11:06:31] local.INFO: Running conference:monitor:start command for activities in (2026-05-11 10:56:00, 2026-05-11 11:01:00] {\"correlation_id\":\"ad10418d-4623-4ff3-b748-2e19330019b3\",\"trace_id\":\"a31c4682-7665-42af-99bd-8ab71b6cd9ac\"}\n[2026-05-11 11:06:31] local.INFO: [conference:monitor:start] start ok {\"activity_id\":484852} {\"correlation_id\":\"ad10418d-4623-4ff3-b748-2e19330019b3\",\"trace_id\":\"a31c4682-7665-42af-99bd-8ab71b6cd9ac\"}\n[2026-05-11 11:06:31] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage for command {\"command\":\"conference:monitor:start\",\"memoryBeforeCommandInMb\":60.0,\"memoryAfterCommandInMB\":60.0,\"memoryPeakBeforeCommandInMb\":99.727,\"memoryPeakAfterCommandInMB\":99.727} {\"correlation_id\":\"ad10418d-4623-4ff3-b748-2e19330019b3\",\"trace_id\":\"a31c4682-7665-42af-99bd-8ab71b6cd9ac\"}\n[2026-05-11 11:06:33] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"conference:monitor:end\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.727} {\"correlation_id\":\"31a09cc3-a94d-4145-ab23-b041b077106a\",\"trace_id\":\"82ffd281-4725-402c-90c7-162549d4415a\"}\n[2026-05-11 11:06:33] local.INFO: conference:monitor:end:Jiminny\\Console\\Commands\\Activities\\MonitorMeetingEndCommand::logActivitiesEnded {\"from\":\"11:01\",\"to\":\"11:06\"} {\"correlation_id\":\"31a09cc3-a94d-4145-ab23-b041b077106a\",\"trace_id\":\"82ffd281-4725-402c-90c7-162549d4415a\"}\n[2026-05-11 11:06:33] local.INFO: conference:monitor:end:Jiminny\\Console\\Commands\\Activities\\MonitorMeetingEndCommand::logActivitiesWithUnfinishedSession {\"from\":\"00:56\",\"to\":\"01:01\"} {\"correlation_id\":\"31a09cc3-a94d-4145-ab23-b041b077106a\",\"trace_id\":\"82ffd281-4725-402c-90c7-162549d4415a\"}\n[2026-05-11 11:06:33] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage for command {\"command\":\"conference:monitor:end\",\"memoryBeforeCommandInMb\":60.0,\"memoryAfterCommandInMB\":60.0,\"memoryPeakBeforeCommandInMb\":99.727,\"memoryPeakAfterCommandInMB\":99.727} {\"correlation_id\":\"31a09cc3-a94d-4145-ab23-b041b077106a\",\"trace_id\":\"82ffd281-4725-402c-90c7-162549d4415a\"}\n[2026-05-11 11:06:35] local.NOTICE: Repairing HubSpot tokens start {\"correlation_id\":\"b78a7ca3-5d72-4b23-8794-4543dd6d8ffc\",\"trace_id\":\"bedc11e2-b932-44b3-a16d-436f13a1254e\"}\n[2026-05-11 11:06:35] local.INFO: Trying to refresh HubSpot token {\"account_id\":59,\"updated_at\":\"2025-10-03 09:32:05\"} {\"correlation_id\":\"b78a7ca3-5d72-4b23-8794-4543dd6d8ffc\",\"trace_id\":\"bedc11e2-b932-44b3-a16d-436f13a1254e\"}\n[2026-05-11 11:06:35] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"b78a7ca3-5d72-4b23-8794-4543dd6d8ffc\",\"trace_id\":\"bedc11e2-b932-44b3-a16d-436f13a1254e\"}\n[2026-05-11 11:06:35] local.INFO: [SocialAccountService] Refreshing token from provider {\"socialAccountId\":59,\"provider\":\"hubspot\",\"refreshToken\":\"97b78f6e2cc49965c00c2492b602b02708b1392551e6b3f113fbaa48992af90b\",\"state\":\"full-refresh\"} {\"correlation_id\":\"b78a7ca3-5d72-4b23-8794-4543dd6d8ffc\",\"trace_id\":\"bedc11e2-b932-44b3-a16d-436f13a1254e\"}\n[2026-05-11 11:06:35] local.ERROR: Failed to refresh HubSpot token {\"account_id\":59,\"updated_at\":\"2025-10-03 09:32:05\",\"reason\":\"missing or invalid refresh token\",\"previous\":\"\"} {\"correlation_id\":\"b78a7ca3-5d72-4b23-8794-4543dd6d8ffc\",\"trace_id\":\"bedc11e2-b932-44b3-a16d-436f13a1254e\"}\n[2026-05-11 11:06:35] local.INFO: Trying to refresh HubSpot token {\"account_id\":306,\"updated_at\":\"2023-11-27 09:30:03\"} {\"correlation_id\":\"b78a7ca3-5d72-4b23-8794-4543dd6d8ffc\",\"trace_id\":\"bedc11e2-b932-44b3-a16d-436f13a1254e\"}\n[2026-05-11 11:06:35] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"b78a7ca3-5d72-4b23-8794-4543dd6d8ffc\",\"trace_id\":\"bedc11e2-b932-44b3-a16d-436f13a1254e\"}\n[2026-05-11 11:06:35] local.INFO: [SocialAccountService] Refreshing token from provider {\"socialAccountId\":306,\"provider\":\"hubspot\",\"refreshToken\":\"6fa6aa8cc641d131231acc3470f5c03cb3b07b2e580fb18f8acb3b1dbb72549b\",\"state\":\"full-refresh\"} {\"correlation_id\":\"b78a7ca3-5d72-4b23-8794-4543dd6d8ffc\",\"trace_id\":\"bedc11e2-b932-44b3-a16d-436f13a1254e\"}\n[2026-05-11 11:06:36] local.ERROR: Failed to refresh HubSpot token {\"account_id\":306,\"updated_at\":\"2023-11-27 09:30:03\",\"reason\":\"missing or invalid refresh token\",\"previous\":\"\"} {\"correlation_id\":\"b78a7ca3-5d72-4b23-8794-4543dd6d8ffc\",\"trace_id\":\"bedc11e2-b932-44b3-a16d-436f13a1254e\"}\n[2026-05-11 11:06:36] local.INFO: Trying to refresh HubSpot token {\"account_id\":1372,\"updated_at\":\"2025-10-02 14:47:06\"} {\"correlation_id\":\"b78a7ca3-5d72-4b23-8794-4543dd6d8ffc\",\"trace_id\":\"bedc11e2-b932-44b3-a16d-436f13a1254e\"}\n[2026-05-11 11:06:36] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"b78a7ca3-5d72-4b23-8794-4543dd6d8ffc\",\"trace_id\":\"bedc11e2-b932-44b3-a16d-436f13a1254e\"}\n[2026-05-11 11:06:36] local.INFO: [SocialAccountService] Refreshing token from provider {\"socialAccountId\":1372,\"provider\":\"hubspot\",\"refreshToken\":\"9aa73948c761da29dce46c177cf9aee1fde483a44169ca38723f9f0597d7a8c4\",\"state\":\"full-refresh\"} {\"correlation_id\":\"b78a7ca3-5d72-4b23-8794-4543dd6d8ffc\",\"trace_id\":\"bedc11e2-b932-44b3-a16d-436f13a1254e\"}\n[2026-05-11 11:06:36] local.ERROR: Failed to refresh HubSpot token {\"account_id\":1372,\"updated_at\":\"2025-10-02 14:47:06\",\"reason\":\"missing or invalid refresh token\",\"previous\":\"\"} {\"correlation_id\":\"b78a7ca3-5d72-4b23-8794-4543dd6d8ffc\",\"trace_id\":\"bedc11e2-b932-44b3-a16d-436f13a1254e\"}\n[2026-05-11 11:06:36] local.NOTICE: Repairing HubSpot tokens end {\"total\":3,\"fixed\":0,\"failed\":3} {\"correlation_id\":\"b78a7ca3-5d72-4b23-8794-4543dd6d8ffc\",\"trace_id\":\"bedc11e2-b932-44b3-a16d-436f13a1254e\"}\n[2026-05-11 11:06:40] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"crm:bullhorn:ping\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.727} {\"correlation_id\":\"7f93fd49-8b24-4f96-a532-a4777ccf3183\",\"trace_id\":\"14797017-4a40-4a20-a018-dfa389b58256\"}\n[2026-05-11 11:06:40] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"conference:pre-meeting-reminder\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.727} {\"correlation_id\":\"00c329ca-e23d-4d27-974c-c9cce4940adf\",\"trace_id\":\"d29b535e-a485-4bb8-bcf4-61cc79b73da5\"}\n[2026-05-11 11:06:40] local.INFO: [HubSpot Journal Command] Starting polling service {\"correlation_id\":\"32a337c8-7047-40f4-b1f4-d3008f51e4da\",\"trace_id\":\"b65ab282-ade3-48d8-afd0-798307edc610\"}\n[2026-05-11 11:06:40] local.INFO: [HubSpot Journal Polling] Service starting {\"memory_limit\":\"256M\",\"max_execution_time\":\"0\",\"initial_memory_mb\":60.0} {\"correlation_id\":\"32a337c8-7047-40f4-b1f4-d3008f51e4da\",\"trace_id\":\"b65ab282-ade3-48d8-afd0-798307edc610\"}\n[2026-05-11 11:06:40] local.INFO: [HubSpot Journal Polling] Acquired polling lock {\"expires_at\":\"2026-05-11T11:08:40.813273Z\"} {\"correlation_id\":\"32a337c8-7047-40f4-b1f4-d3008f51e4da\",\"trace_id\":\"b65ab282-ade3-48d8-afd0-798307edc610\"}\n[2026-05-11 11:06:40] local.INFO: [HubSpot Journal Auth] Requesting new client credentials token {\"correlation_id\":\"32a337c8-7047-40f4-b1f4-d3008f51e4da\",\"trace_id\":\"b65ab282-ade3-48d8-afd0-798307edc610\"}\n[2026-05-11 11:06:40] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage for command {\"command\":\"crm:bullhorn:ping\",\"memoryBeforeCommandInMb\":60.0,\"memoryAfterCommandInMB\":60.0,\"memoryPeakBeforeCommandInMb\":99.727,\"memoryPeakAfterCommandInMB\":99.727} {\"correlation_id\":\"7f93fd49-8b24-4f96-a532-a4777ccf3183\",\"trace_id\":\"14797017-4a40-4a20-a018-dfa389b58256\"}\n[2026-05-11 11:06:40] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage for command {\"command\":\"conference:pre-meeting-reminder\",\"memoryBeforeCommandInMb\":60.0,\"memoryAfterCommandInMB\":60.0,\"memoryPeakBeforeCommandInMb\":99.727,\"memoryPeakAfterCommandInMB\":99.727} {\"correlation_id\":\"00c329ca-e23d-4d27-974c-c9cce4940adf\",\"trace_id\":\"d29b535e-a485-4bb8-bcf4-61cc79b73da5\"}\n[2026-05-11 11:06:41] local.INFO: [HubSpot Journal Auth] Successfully obtained new access token {\"expires_in\":1800,\"cached_for\":1500} {\"correlation_id\":\"32a337c8-7047-40f4-b1f4-d3008f51e4da\",\"trace_id\":\"b65ab282-ade3-48d8-afd0-798307edc610\"}\n[2026-05-11 11:06:41] local.INFO: [HubSpot Journal Polling] No data {\"correlation_id\":\"32a337c8-7047-40f4-b1f4-d3008f51e4da\",\"trace_id\":\"b65ab282-ade3-48d8-afd0-798307edc610\"}\n[2026-05-11 11:06:46] local.INFO: [HubSpot Journal Polling] No data {\"correlation_id\":\"32a337c8-7047-40f4-b1f4-d3008f51e4da\",\"trace_id\":\"b65ab282-ade3-48d8-afd0-798307edc610\"}","depth":4,"on_screen":true,"value":"[2026-05-11 11:06:08] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":612562,\"team_id\":2,\"email\":\"horen.kirazyan@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"c1949c5e-c0f4-431e-ba26-59d49408d871\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:08] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":612562,\"participants_processed\":2,\"exact_matches\":0,\"domain_matches\":1,\"best_match_found\":true} {\"correlation_id\":\"c1949c5e-c0f4-431e-ba26-59d49408d871\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:08] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612562} {\"correlation_id\":\"c1949c5e-c0f4-431e-ba26-59d49408d871\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:08] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612562,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"c1949c5e-c0f4-431e-ba26-59d49408d871\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:08] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":612562,\"remote_search\":true,\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89} {\"correlation_id\":\"c1949c5e-c0f4-431e-ba26-59d49408d871\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:09] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":612561,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89}} {\"correlation_id\":\"b715f531-04da-410f-be31-055aa1c13754\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:09] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612561} {\"correlation_id\":\"b715f531-04da-410f-be31-055aa1c13754\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:09] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612561,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"b715f531-04da-410f-be31-055aa1c13754\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:09] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":612561,\"participants\":[{\"id\":999780,\"user_id\":206,\"contact_id\":null,\"lead_id\":null},{\"id\":999781,\"user_id\":null,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"b715f531-04da-410f-be31-055aa1c13754\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:09] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"b715f531-04da-410f-be31-055aa1c13754\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:09] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"b715f531-04da-410f-be31-055aa1c13754\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:09] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"b715f531-04da-410f-be31-055aa1c13754\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:09] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"b715f531-04da-410f-be31-055aa1c13754\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:09] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":612561,\"team_id\":2,\"email\":\"horen.kirazyan@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"b715f531-04da-410f-be31-055aa1c13754\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:09] local.INFO: [Prospect match] Cache miss, calling the API {\"identifier_type\":\"email\",\"identifier\":\"447700174614.447782589921.OeREojLVnk@txt.staging.jiminny.com\"} {\"correlation_id\":\"b715f531-04da-410f-be31-055aa1c13754\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:10] local.INFO: [Hubspot] Failed to fetch contact {\"email\":\"447700174614.447782589921.OeREojLVnk@txt.staging.jiminny.com\",\"reason\":\"[404] Client error: `GET https://api.hubapi.com/crm/v3/objects/contacts/447700174614.447782589921.OeREojLVnk%40txt.staging.jiminny.com?properties=email%2Cfirstname%2Clastname%2Ccountry%2Cphone%2Cmobilephone%2Cjobtitle%2Chubspot_owner_id%2Cassociatedcompanyid%2Cphoto&archived=0&idProperty=email` resulted in a `404 Not Found` response\"} {\"correlation_id\":\"b715f531-04da-410f-be31-055aa1c13754\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:10] local.INFO: [Prospect match] API returned empty result, caching the miss with empty prospect data {\"identifier_type\":\"email\",\"identifier\":\"447700174614.447782589921.OeREojLVnk@txt.staging.jiminny.com\"} {\"correlation_id\":\"b715f531-04da-410f-be31-055aa1c13754\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:10] local.INFO: [Prospect match] Resolved company domain from email {\"email\":\"447700174614.447782589921.OeREojLVnk@txt.staging.jiminny.com\",\"domain\":\"jiminny.com\"} {\"correlation_id\":\"b715f531-04da-410f-be31-055aa1c13754\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:10] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":612561,\"participants_processed\":2,\"exact_matches\":0,\"domain_matches\":1,\"best_match_found\":true} {\"correlation_id\":\"b715f531-04da-410f-be31-055aa1c13754\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:10] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612561} {\"correlation_id\":\"b715f531-04da-410f-be31-055aa1c13754\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:10] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612561,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"b715f531-04da-410f-be31-055aa1c13754\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:10] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":612561,\"remote_search\":true,\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89} {\"correlation_id\":\"b715f531-04da-410f-be31-055aa1c13754\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:10] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":612560,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89}} {\"correlation_id\":\"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:10] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612560} {\"correlation_id\":\"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:10] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612560,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:10] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":612560,\"participants\":[{\"id\":999778,\"user_id\":null,\"contact_id\":null,\"lead_id\":null},{\"id\":999779,\"user_id\":206,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"447782589921@txt.staging.jiminny.com\"} {\"correlation_id\":\"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [Prospect match] cached empty result - no API calls, try next matching method {\"identifier_type\":\"email\",\"identifier\":\"447782589921@txt.staging.jiminny.com\"} {\"correlation_id\":\"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [Prospect match] Resolved company domain from email {\"email\":\"447782589921@txt.staging.jiminny.com\",\"domain\":\"jiminny.com\"} {\"correlation_id\":\"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":612560,\"team_id\":2,\"email\":\"horen.kirazyan@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":612560,\"participants_processed\":2,\"exact_matches\":0,\"domain_matches\":1,\"best_match_found\":true} {\"correlation_id\":\"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612560} {\"correlation_id\":\"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [ EsUpdateProcessManager ] Finished updating entities in ES {\"worker\":\"\",\"peak_memory\":\"99.73 MB\",\"elapsed_seconds\":0.74,\"update_target\":\"activities\",\"should_iterate_again\":false} {\"correlation_id\":\"38935ae1-fcb3-437e-9c9e-1b7415a0b7e1\",\"trace_id\":\"325a2f9b-9c66-4223-ac02-1a43723f4315\"}\n[2026-05-11 11:06:11] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612560,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":612560,\"remote_search\":true,\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89} {\"correlation_id\":\"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":612559,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":97,\"account_id\":69,\"opportunity_id\":5011,\"stage_id\":34}} {\"correlation_id\":\"4d59a59c-38a5-4a1d-9379-6ceaf67b747e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612559} {\"correlation_id\":\"4d59a59c-38a5-4a1d-9379-6ceaf67b747e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612559,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"4d59a59c-38a5-4a1d-9379-6ceaf67b747e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":612559,\"participants\":[{\"id\":999776,\"user_id\":206,\"contact_id\":null,\"lead_id\":null},{\"id\":999777,\"user_id\":null,\"contact_id\":97,\"lead_id\":null}]} {\"correlation_id\":\"4d59a59c-38a5-4a1d-9379-6ceaf67b747e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"4d59a59c-38a5-4a1d-9379-6ceaf67b747e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"4d59a59c-38a5-4a1d-9379-6ceaf67b747e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"4d59a59c-38a5-4a1d-9379-6ceaf67b747e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"4d59a59c-38a5-4a1d-9379-6ceaf67b747e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":612559,\"team_id\":2,\"email\":\"horen.kirazyan@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"4d59a59c-38a5-4a1d-9379-6ceaf67b747e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: ProspectCache - Searching DB for opportunity by owner {\"account_id\":69,\"contact_id\":97,\"owner_id\":206} {\"correlation_id\":\"4d59a59c-38a5-4a1d-9379-6ceaf67b747e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: ProspectCache - Fallback DB opportunity search {\"account_id\":69,\"contact_id\":97} {\"correlation_id\":\"4d59a59c-38a5-4a1d-9379-6ceaf67b747e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: ProspectCache - Opportunity DB search results {\"account_id\":69,\"contact_id\":97,\"opportunity_id\":5011} {\"correlation_id\":\"4d59a59c-38a5-4a1d-9379-6ceaf67b747e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"robinsoncrusoe@test.com\"} {\"correlation_id\":\"4d59a59c-38a5-4a1d-9379-6ceaf67b747e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":612559,\"participants_processed\":2,\"exact_matches\":1,\"domain_matches\":0,\"best_match_found\":true} {\"correlation_id\":\"4d59a59c-38a5-4a1d-9379-6ceaf67b747e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612559} {\"correlation_id\":\"4d59a59c-38a5-4a1d-9379-6ceaf67b747e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612559,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"4d59a59c-38a5-4a1d-9379-6ceaf67b747e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":612559,\"remote_search\":true,\"lead_id\":null,\"contact_id\":97,\"account_id\":69,\"opportunity_id\":5011,\"stage_id\":34} {\"correlation_id\":\"4d59a59c-38a5-4a1d-9379-6ceaf67b747e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:12] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":612558,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":97,\"account_id\":69,\"opportunity_id\":5011,\"stage_id\":34}} {\"correlation_id\":\"a105800d-d2f7-4772-a534-7791e262461c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:12] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612558} {\"correlation_id\":\"a105800d-d2f7-4772-a534-7791e262461c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:12] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612558,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"a105800d-d2f7-4772-a534-7791e262461c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:12] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":612558,\"participants\":[{\"id\":999774,\"user_id\":206,\"contact_id\":null,\"lead_id\":null},{\"id\":999775,\"user_id\":null,\"contact_id\":97,\"lead_id\":null}]} {\"correlation_id\":\"a105800d-d2f7-4772-a534-7791e262461c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:12] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"a105800d-d2f7-4772-a534-7791e262461c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:12] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"a105800d-d2f7-4772-a534-7791e262461c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:12] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"a105800d-d2f7-4772-a534-7791e262461c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:12] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"a105800d-d2f7-4772-a534-7791e262461c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:12] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":612558,\"team_id\":2,\"email\":\"horen.kirazyan@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"a105800d-d2f7-4772-a534-7791e262461c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:12] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"robinsoncrusoe@test.com\"} {\"correlation_id\":\"a105800d-d2f7-4772-a534-7791e262461c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:12] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":612558,\"participants_processed\":2,\"exact_matches\":1,\"domain_matches\":0,\"best_match_found\":true} {\"correlation_id\":\"a105800d-d2f7-4772-a534-7791e262461c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:12] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612558} {\"correlation_id\":\"a105800d-d2f7-4772-a534-7791e262461c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:12] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612558,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"a105800d-d2f7-4772-a534-7791e262461c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:12] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":612558,\"remote_search\":true,\"lead_id\":null,\"contact_id\":97,\"account_id\":69,\"opportunity_id\":5011,\"stage_id\":34} {\"correlation_id\":\"a105800d-d2f7-4772-a534-7791e262461c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:13] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":612557,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":97,\"account_id\":69,\"opportunity_id\":5011,\"stage_id\":34}} {\"correlation_id\":\"3ec57437-5796-4e44-8bf0-aaec149ac813\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:13] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612557} {\"correlation_id\":\"3ec57437-5796-4e44-8bf0-aaec149ac813\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:13] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612557,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"3ec57437-5796-4e44-8bf0-aaec149ac813\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:13] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":612557,\"participants\":[{\"id\":999772,\"user_id\":206,\"contact_id\":null,\"lead_id\":null},{\"id\":999773,\"user_id\":null,\"contact_id\":97,\"lead_id\":null}]} {\"correlation_id\":\"3ec57437-5796-4e44-8bf0-aaec149ac813\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:13] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"mailbox:skip-lists:refresh\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.727} {\"correlation_id\":\"5203040f-23be-43e6-8410-ba49ee1b38b4\",\"trace_id\":\"e787953d-9b78-4b63-a987-4f93f6892984\"}\n[2026-05-11 11:06:13] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"3ec57437-5796-4e44-8bf0-aaec149ac813\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:13] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"3ec57437-5796-4e44-8bf0-aaec149ac813\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:13] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"3ec57437-5796-4e44-8bf0-aaec149ac813\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:13] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"3ec57437-5796-4e44-8bf0-aaec149ac813\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:13] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":612557,\"team_id\":2,\"email\":\"horen.kirazyan@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"3ec57437-5796-4e44-8bf0-aaec149ac813\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:13] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"robinsoncrusoe@test.com\"} {\"correlation_id\":\"3ec57437-5796-4e44-8bf0-aaec149ac813\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:13] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":612557,\"participants_processed\":2,\"exact_matches\":1,\"domain_matches\":0,\"best_match_found\":true} {\"correlation_id\":\"3ec57437-5796-4e44-8bf0-aaec149ac813\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:13] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612557} {\"correlation_id\":\"3ec57437-5796-4e44-8bf0-aaec149ac813\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:13] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612557,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"3ec57437-5796-4e44-8bf0-aaec149ac813\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:13] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":612557,\"remote_search\":true,\"lead_id\":null,\"contact_id\":97,\"account_id\":69,\"opportunity_id\":5011,\"stage_id\":34} {\"correlation_id\":\"3ec57437-5796-4e44-8bf0-aaec149ac813\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:13] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage for command {\"command\":\"mailbox:skip-lists:refresh\",\"memoryBeforeCommandInMb\":60.0,\"memoryAfterCommandInMB\":60.0,\"memoryPeakBeforeCommandInMb\":99.727,\"memoryPeakAfterCommandInMB\":99.727} {\"correlation_id\":\"5203040f-23be-43e6-8410-ba49ee1b38b4\",\"trace_id\":\"e787953d-9b78-4b63-a987-4f93f6892984\"}\n[2026-05-11 11:06:13] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":612556,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":97,\"account_id\":69,\"opportunity_id\":5011,\"stage_id\":34}} {\"correlation_id\":\"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:13] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612556} {\"correlation_id\":\"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:13] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612556,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:13] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":612556,\"participants\":[{\"id\":999770,\"user_id\":206,\"contact_id\":null,\"lead_id\":null},{\"id\":999771,\"user_id\":null,\"contact_id\":97,\"lead_id\":null}]} {\"correlation_id\":\"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:13] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:13] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:13] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:13] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":612556,\"team_id\":2,\"email\":\"horen.kirazyan@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"robinsoncrusoe@test.com\"} {\"correlation_id\":\"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":612556,\"participants_processed\":2,\"exact_matches\":1,\"domain_matches\":0,\"best_match_found\":true} {\"correlation_id\":\"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612556} {\"correlation_id\":\"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612556,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":612556,\"remote_search\":true,\"lead_id\":null,\"contact_id\":97,\"account_id\":69,\"opportunity_id\":5011,\"stage_id\":34} {\"correlation_id\":\"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":612555,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":97,\"account_id\":69,\"opportunity_id\":5011,\"stage_id\":34}} {\"correlation_id\":\"3e9d1a70-5491-44ae-8f28-5cb9b60e1859\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612555} {\"correlation_id\":\"3e9d1a70-5491-44ae-8f28-5cb9b60e1859\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612555,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"3e9d1a70-5491-44ae-8f28-5cb9b60e1859\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":612555,\"participants\":[{\"id\":999768,\"user_id\":206,\"contact_id\":null,\"lead_id\":null},{\"id\":999769,\"user_id\":null,\"contact_id\":97,\"lead_id\":null}]} {\"correlation_id\":\"3e9d1a70-5491-44ae-8f28-5cb9b60e1859\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"3e9d1a70-5491-44ae-8f28-5cb9b60e1859\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"3e9d1a70-5491-44ae-8f28-5cb9b60e1859\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"3e9d1a70-5491-44ae-8f28-5cb9b60e1859\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"3e9d1a70-5491-44ae-8f28-5cb9b60e1859\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":612555,\"team_id\":2,\"email\":\"horen.kirazyan@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"3e9d1a70-5491-44ae-8f28-5cb9b60e1859\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"robinsoncrusoe@test.com\"} {\"correlation_id\":\"3e9d1a70-5491-44ae-8f28-5cb9b60e1859\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":612555,\"participants_processed\":2,\"exact_matches\":1,\"domain_matches\":0,\"best_match_found\":true} {\"correlation_id\":\"3e9d1a70-5491-44ae-8f28-5cb9b60e1859\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612555} {\"correlation_id\":\"3e9d1a70-5491-44ae-8f28-5cb9b60e1859\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612555,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"3e9d1a70-5491-44ae-8f28-5cb9b60e1859\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":612555,\"remote_search\":true,\"lead_id\":null,\"contact_id\":97,\"account_id\":69,\"opportunity_id\":5011,\"stage_id\":34} {\"correlation_id\":\"3e9d1a70-5491-44ae-8f28-5cb9b60e1859\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":612554,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":97,\"account_id\":69,\"opportunity_id\":5011,\"stage_id\":34}} {\"correlation_id\":\"7101e979-3a7e-4b2c-bf04-dc6a24ba1356\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612554} {\"correlation_id\":\"7101e979-3a7e-4b2c-bf04-dc6a24ba1356\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612554,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"7101e979-3a7e-4b2c-bf04-dc6a24ba1356\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":612554,\"participants\":[{\"id\":999766,\"user_id\":206,\"contact_id\":null,\"lead_id\":null},{\"id\":999767,\"user_id\":null,\"contact_id\":97,\"lead_id\":null}]} {\"correlation_id\":\"7101e979-3a7e-4b2c-bf04-dc6a24ba1356\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"7101e979-3a7e-4b2c-bf04-dc6a24ba1356\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"7101e979-3a7e-4b2c-bf04-dc6a24ba1356\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"7101e979-3a7e-4b2c-bf04-dc6a24ba1356\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"7101e979-3a7e-4b2c-bf04-dc6a24ba1356\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":612554,\"team_id\":2,\"email\":\"horen.kirazyan@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"7101e979-3a7e-4b2c-bf04-dc6a24ba1356\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"robinsoncrusoe@test.com\"} {\"correlation_id\":\"7101e979-3a7e-4b2c-bf04-dc6a24ba1356\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":612554,\"participants_processed\":2,\"exact_matches\":1,\"domain_matches\":0,\"best_match_found\":true} {\"correlation_id\":\"7101e979-3a7e-4b2c-bf04-dc6a24ba1356\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612554} {\"correlation_id\":\"7101e979-3a7e-4b2c-bf04-dc6a24ba1356\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612554,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"7101e979-3a7e-4b2c-bf04-dc6a24ba1356\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":612554,\"remote_search\":true,\"lead_id\":null,\"contact_id\":97,\"account_id\":69,\"opportunity_id\":5011,\"stage_id\":34} {\"correlation_id\":\"7101e979-3a7e-4b2c-bf04-dc6a24ba1356\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":612553,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":97,\"account_id\":69,\"opportunity_id\":5011,\"stage_id\":34}} {\"correlation_id\":\"346dc229-757b-496c-aceb-d2ddb367c7d6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612553} {\"correlation_id\":\"346dc229-757b-496c-aceb-d2ddb367c7d6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612553,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"346dc229-757b-496c-aceb-d2ddb367c7d6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":612553,\"participants\":[{\"id\":999764,\"user_id\":206,\"contact_id\":null,\"lead_id\":null},{\"id\":999765,\"user_id\":null,\"contact_id\":97,\"lead_id\":null}]} {\"correlation_id\":\"346dc229-757b-496c-aceb-d2ddb367c7d6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"346dc229-757b-496c-aceb-d2ddb367c7d6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"346dc229-757b-496c-aceb-d2ddb367c7d6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"346dc229-757b-496c-aceb-d2ddb367c7d6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"346dc229-757b-496c-aceb-d2ddb367c7d6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":612553,\"team_id\":2,\"email\":\"horen.kirazyan@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"346dc229-757b-496c-aceb-d2ddb367c7d6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"robinsoncrusoe@test.com\"} {\"correlation_id\":\"346dc229-757b-496c-aceb-d2ddb367c7d6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":612553,\"participants_processed\":2,\"exact_matches\":1,\"domain_matches\":0,\"best_match_found\":true} {\"correlation_id\":\"346dc229-757b-496c-aceb-d2ddb367c7d6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612553} {\"correlation_id\":\"346dc229-757b-496c-aceb-d2ddb367c7d6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612553,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"346dc229-757b-496c-aceb-d2ddb367c7d6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":612553,\"remote_search\":true,\"lead_id\":null,\"contact_id\":97,\"account_id\":69,\"opportunity_id\":5011,\"stage_id\":34} {\"correlation_id\":\"346dc229-757b-496c-aceb-d2ddb367c7d6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":612552,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":97,\"account_id\":69,\"opportunity_id\":5011,\"stage_id\":34}} {\"correlation_id\":\"56ef8656-0bfa-4f11-a245-9254b8179555\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612552} {\"correlation_id\":\"56ef8656-0bfa-4f11-a245-9254b8179555\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612552,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"56ef8656-0bfa-4f11-a245-9254b8179555\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":612552,\"participants\":[{\"id\":999762,\"user_id\":206,\"contact_id\":null,\"lead_id\":null},{\"id\":999763,\"user_id\":null,\"contact_id\":97,\"lead_id\":null}]} {\"correlation_id\":\"56ef8656-0bfa-4f11-a245-9254b8179555\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"56ef8656-0bfa-4f11-a245-9254b8179555\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"56ef8656-0bfa-4f11-a245-9254b8179555\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"56ef8656-0bfa-4f11-a245-9254b8179555\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"56ef8656-0bfa-4f11-a245-9254b8179555\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":612552,\"team_id\":2,\"email\":\"horen.kirazyan@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"56ef8656-0bfa-4f11-a245-9254b8179555\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"robinsoncrusoe@test.com\"} {\"correlation_id\":\"56ef8656-0bfa-4f11-a245-9254b8179555\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":612552,\"participants_processed\":2,\"exact_matches\":1,\"domain_matches\":0,\"best_match_found\":true} {\"correlation_id\":\"56ef8656-0bfa-4f11-a245-9254b8179555\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612552} {\"correlation_id\":\"56ef8656-0bfa-4f11-a245-9254b8179555\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612552,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"56ef8656-0bfa-4f11-a245-9254b8179555\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":612552,\"remote_search\":true,\"lead_id\":null,\"contact_id\":97,\"account_id\":69,\"opportunity_id\":5011,\"stage_id\":34} {\"correlation_id\":\"56ef8656-0bfa-4f11-a245-9254b8179555\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":612551,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":97,\"account_id\":69,\"opportunity_id\":5011,\"stage_id\":34}} {\"correlation_id\":\"df5e35c3-b4ae-4eba-9108-cd30ac152335\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612551} {\"correlation_id\":\"df5e35c3-b4ae-4eba-9108-cd30ac152335\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612551,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"df5e35c3-b4ae-4eba-9108-cd30ac152335\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":612551,\"participants\":[{\"id\":999760,\"user_id\":206,\"contact_id\":null,\"lead_id\":null},{\"id\":999761,\"user_id\":null,\"contact_id\":97,\"lead_id\":null}]} {\"correlation_id\":\"df5e35c3-b4ae-4eba-9108-cd30ac152335\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"df5e35c3-b4ae-4eba-9108-cd30ac152335\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"df5e35c3-b4ae-4eba-9108-cd30ac152335\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"df5e35c3-b4ae-4eba-9108-cd30ac152335\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"df5e35c3-b4ae-4eba-9108-cd30ac152335\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":612551,\"team_id\":2,\"email\":\"horen.kirazyan@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"df5e35c3-b4ae-4eba-9108-cd30ac152335\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"robinsoncrusoe@test.com\"} {\"correlation_id\":\"df5e35c3-b4ae-4eba-9108-cd30ac152335\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":612551,\"participants_processed\":2,\"exact_matches\":1,\"domain_matches\":0,\"best_match_found\":true} {\"correlation_id\":\"df5e35c3-b4ae-4eba-9108-cd30ac152335\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612551} {\"correlation_id\":\"df5e35c3-b4ae-4eba-9108-cd30ac152335\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612551,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"df5e35c3-b4ae-4eba-9108-cd30ac152335\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":612551,\"remote_search\":true,\"lead_id\":null,\"contact_id\":97,\"account_id\":69,\"opportunity_id\":5011,\"stage_id\":34} {\"correlation_id\":\"df5e35c3-b4ae-4eba-9108-cd30ac152335\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":612550,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":97,\"account_id\":69,\"opportunity_id\":5011,\"stage_id\":34}} {\"correlation_id\":\"64768905-983e-4f4e-a368-901931486f34\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612550} {\"correlation_id\":\"64768905-983e-4f4e-a368-901931486f34\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612550,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"64768905-983e-4f4e-a368-901931486f34\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":612550,\"participants\":[{\"id\":999758,\"user_id\":206,\"contact_id\":null,\"lead_id\":null},{\"id\":999759,\"user_id\":null,\"contact_id\":97,\"lead_id\":null}]} {\"correlation_id\":\"64768905-983e-4f4e-a368-901931486f34\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"64768905-983e-4f4e-a368-901931486f34\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"64768905-983e-4f4e-a368-901931486f34\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"64768905-983e-4f4e-a368-901931486f34\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"64768905-983e-4f4e-a368-901931486f34\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":612550,\"team_id\":2,\"email\":\"horen.kirazyan@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"64768905-983e-4f4e-a368-901931486f34\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"robinsoncrusoe@test.com\"} {\"correlation_id\":\"64768905-983e-4f4e-a368-901931486f34\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":612550,\"participants_processed\":2,\"exact_matches\":1,\"domain_matches\":0,\"best_match_found\":true} {\"correlation_id\":\"64768905-983e-4f4e-a368-901931486f34\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612550} {\"correlation_id\":\"64768905-983e-4f4e-a368-901931486f34\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612550,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"64768905-983e-4f4e-a368-901931486f34\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":612550,\"remote_search\":true,\"lead_id\":null,\"contact_id\":97,\"account_id\":69,\"opportunity_id\":5011,\"stage_id\":34} {\"correlation_id\":\"64768905-983e-4f4e-a368-901931486f34\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":612549,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":97,\"account_id\":69,\"opportunity_id\":5011,\"stage_id\":34}} {\"correlation_id\":\"97c9609f-bef0-4e95-8301-9f5549d29c1e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612549} {\"correlation_id\":\"97c9609f-bef0-4e95-8301-9f5549d29c1e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612549,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"97c9609f-bef0-4e95-8301-9f5549d29c1e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":612549,\"participants\":[{\"id\":999756,\"user_id\":206,\"contact_id\":null,\"lead_id\":null},{\"id\":999757,\"user_id\":null,\"contact_id\":97,\"lead_id\":null}]} {\"correlation_id\":\"97c9609f-bef0-4e95-8301-9f5549d29c1e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"97c9609f-bef0-4e95-8301-9f5549d29c1e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"97c9609f-bef0-4e95-8301-9f5549d29c1e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"97c9609f-bef0-4e95-8301-9f5549d29c1e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"97c9609f-bef0-4e95-8301-9f5549d29c1e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":612549,\"team_id\":2,\"email\":\"horen.kirazyan@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"97c9609f-bef0-4e95-8301-9f5549d29c1e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"robinsoncrusoe@test.com\"} {\"correlation_id\":\"97c9609f-bef0-4e95-8301-9f5549d29c1e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":612549,\"participants_processed\":2,\"exact_matches\":1,\"domain_matches\":0,\"best_match_found\":true} {\"correlation_id\":\"97c9609f-bef0-4e95-8301-9f5549d29c1e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612549} {\"correlation_id\":\"97c9609f-bef0-4e95-8301-9f5549d29c1e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612549,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"97c9609f-bef0-4e95-8301-9f5549d29c1e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":612549,\"remote_search\":true,\"lead_id\":null,\"contact_id\":97,\"account_id\":69,\"opportunity_id\":5011,\"stage_id\":34} {\"correlation_id\":\"97c9609f-bef0-4e95-8301-9f5549d29c1e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":612365,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":4491,\"account_id\":243,\"opportunity_id\":276,\"stage_id\":36}} {\"correlation_id\":\"7761b4e0-c2d4-4805-8d1d-26728cbffb07\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612365} {\"correlation_id\":\"7761b4e0-c2d4-4805-8d1d-26728cbffb07\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612365,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"7761b4e0-c2d4-4805-8d1d-26728cbffb07\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":612365,\"participants\":[{\"id\":999563,\"user_id\":null,\"contact_id\":4491,\"lead_id\":null},{\"id\":999564,\"user_id\":206,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"7761b4e0-c2d4-4805-8d1d-26728cbffb07\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"7761b4e0-c2d4-4805-8d1d-26728cbffb07\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"7761b4e0-c2d4-4805-8d1d-26728cbffb07\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"7761b4e0-c2d4-4805-8d1d-26728cbffb07\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"7761b4e0-c2d4-4805-8d1d-26728cbffb07\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"horencho@gmail.com\"} {\"correlation_id\":\"7761b4e0-c2d4-4805-8d1d-26728cbffb07\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":612365,\"team_id\":2,\"email\":\"horen.kirazyan@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"7761b4e0-c2d4-4805-8d1d-26728cbffb07\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":612365,\"participants_processed\":2,\"exact_matches\":1,\"domain_matches\":0,\"best_match_found\":true} {\"correlation_id\":\"7761b4e0-c2d4-4805-8d1d-26728cbffb07\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612365} {\"correlation_id\":\"7761b4e0-c2d4-4805-8d1d-26728cbffb07\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612365,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"7761b4e0-c2d4-4805-8d1d-26728cbffb07\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":612365,\"remote_search\":true,\"lead_id\":null,\"contact_id\":4491,\"account_id\":243,\"opportunity_id\":276,\"stage_id\":36} {\"correlation_id\":\"7761b4e0-c2d4-4805-8d1d-26728cbffb07\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":612360,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":4491,\"account_id\":243,\"opportunity_id\":276,\"stage_id\":36}} {\"correlation_id\":\"b8b554cd-4830-4ac5-be75-595cc7eb6ca8\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612360} {\"correlation_id\":\"b8b554cd-4830-4ac5-be75-595cc7eb6ca8\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612360,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"b8b554cd-4830-4ac5-be75-595cc7eb6ca8\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":612360,\"participants\":[{\"id\":999552,\"user_id\":null,\"contact_id\":4491,\"lead_id\":null},{\"id\":999553,\"user_id\":206,\"contact_id\":null,\"lead_id\":null},{\"id\":999565,\"user_id\":null,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"b8b554cd-4830-4ac5-be75-595cc7eb6ca8\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"b8b554cd-4830-4ac5-be75-595cc7eb6ca8\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"b8b554cd-4830-4ac5-be75-595cc7eb6ca8\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"b8b554cd-4830-4ac5-be75-595cc7eb6ca8\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"b8b554cd-4830-4ac5-be75-595cc7eb6ca8\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"horencho@gmail.com\"} {\"correlation_id\":\"b8b554cd-4830-4ac5-be75-595cc7eb6ca8\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":612360,\"team_id\":2,\"email\":\"horen.kirazyan@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"b8b554cd-4830-4ac5-be75-595cc7eb6ca8\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [Hubspot] Pagination completed {\"team_id\":2,\"endpoint\":\"https://api.hubapi.com/crm/v3/objects/contact/search\",\"total_requests\":1,\"total_records_fetched\":0,\"total_elapsed_seconds\":0.31,\"average_seconds_per_request\":0.31} {\"correlation_id\":\"b8b554cd-4830-4ac5-be75-595cc7eb6ca8\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":612360,\"participants_processed\":3,\"exact_matches\":1,\"domain_matches\":0,\"best_match_found\":true} {\"correlation_id\":\"b8b554cd-4830-4ac5-be75-595cc7eb6ca8\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612360} {\"correlation_id\":\"b8b554cd-4830-4ac5-be75-595cc7eb6ca8\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612360,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"b8b554cd-4830-4ac5-be75-595cc7eb6ca8\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":612360,\"remote_search\":true,\"lead_id\":null,\"contact_id\":4491,\"account_id\":243,\"opportunity_id\":276,\"stage_id\":36} {\"correlation_id\":\"b8b554cd-4830-4ac5-be75-595cc7eb6ca8\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":612340,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":4491,\"account_id\":243,\"opportunity_id\":276,\"stage_id\":36}} {\"correlation_id\":\"9a47ab79-dbb3-46e2-ad27-3a5120c080ef\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612340} {\"correlation_id\":\"9a47ab79-dbb3-46e2-ad27-3a5120c080ef\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612340,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"9a47ab79-dbb3-46e2-ad27-3a5120c080ef\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":612340,\"participants\":[{\"id\":999516,\"user_id\":null,\"contact_id\":4491,\"lead_id\":null},{\"id\":999517,\"user_id\":206,\"contact_id\":null,\"lead_id\":null},{\"id\":999518,\"user_id\":null,\"contact_id\":null,\"lead_id\":null},{\"id\":999519,\"user_id\":null,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"9a47ab79-dbb3-46e2-ad27-3a5120c080ef\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"9a47ab79-dbb3-46e2-ad27-3a5120c080ef\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"9a47ab79-dbb3-46e2-ad27-3a5120c080ef\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"9a47ab79-dbb3-46e2-ad27-3a5120c080ef\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"9a47ab79-dbb3-46e2-ad27-3a5120c080ef\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"horencho@gmail.com\"} {\"correlation_id\":\"9a47ab79-dbb3-46e2-ad27-3a5120c080ef\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":612340,\"team_id\":2,\"email\":\"horen.kirazyan@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"9a47ab79-dbb3-46e2-ad27-3a5120c080ef\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [Hubspot] Pagination completed {\"team_id\":2,\"endpoint\":\"https://api.hubapi.com/crm/v3/objects/contact/search\",\"total_requests\":1,\"total_records_fetched\":0,\"total_elapsed_seconds\":0.24,\"average_seconds_per_request\":0.24} {\"correlation_id\":\"9a47ab79-dbb3-46e2-ad27-3a5120c080ef\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":612340,\"participants_processed\":4,\"exact_matches\":1,\"domain_matches\":0,\"best_match_found\":true} {\"correlation_id\":\"9a47ab79-dbb3-46e2-ad27-3a5120c080ef\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [ EsUpdateProcessManager ] Finished updating entities in ES {\"worker\":\"\",\"peak_memory\":\"99.73 MB\",\"elapsed_seconds\":0.45,\"update_target\":\"activities\",\"should_iterate_again\":false} {\"correlation_id\":\"38935ae1-fcb3-437e-9c9e-1b7415a0b7e1\",\"trace_id\":\"325a2f9b-9c66-4223-ac02-1a43723f4315\"}\n[2026-05-11 11:06:16] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612340} {\"correlation_id\":\"9a47ab79-dbb3-46e2-ad27-3a5120c080ef\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612340,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"9a47ab79-dbb3-46e2-ad27-3a5120c080ef\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":612340,\"remote_search\":true,\"lead_id\":null,\"contact_id\":4491,\"account_id\":243,\"opportunity_id\":276,\"stage_id\":36} {\"correlation_id\":\"9a47ab79-dbb3-46e2-ad27-3a5120c080ef\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":612339,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":4491,\"account_id\":243,\"opportunity_id\":276,\"stage_id\":36}} {\"correlation_id\":\"eae16400-35f9-4885-b5fc-edb8872350d4\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612339} {\"correlation_id\":\"eae16400-35f9-4885-b5fc-edb8872350d4\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612339,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"eae16400-35f9-4885-b5fc-edb8872350d4\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":612339,\"participants\":[{\"id\":999514,\"user_id\":null,\"contact_id\":4491,\"lead_id\":null},{\"id\":999515,\"user_id\":206,\"contact_id\":null,\"lead_id\":null},{\"id\":999540,\"user_id\":null,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"eae16400-35f9-4885-b5fc-edb8872350d4\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"eae16400-35f9-4885-b5fc-edb8872350d4\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"eae16400-35f9-4885-b5fc-edb8872350d4\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"eae16400-35f9-4885-b5fc-edb8872350d4\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"eae16400-35f9-4885-b5fc-edb8872350d4\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"horencho@gmail.com\"} {\"correlation_id\":\"eae16400-35f9-4885-b5fc-edb8872350d4\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":612339,\"team_id\":2,\"email\":\"horen.kirazyan@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"eae16400-35f9-4885-b5fc-edb8872350d4\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":612339,\"participants_processed\":3,\"exact_matches\":1,\"domain_matches\":0,\"best_match_found\":true} {\"correlation_id\":\"eae16400-35f9-4885-b5fc-edb8872350d4\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612339} {\"correlation_id\":\"eae16400-35f9-4885-b5fc-edb8872350d4\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612339,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"eae16400-35f9-4885-b5fc-edb8872350d4\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":612339,\"remote_search\":true,\"lead_id\":null,\"contact_id\":4491,\"account_id\":243,\"opportunity_id\":276,\"stage_id\":36} {\"correlation_id\":\"eae16400-35f9-4885-b5fc-edb8872350d4\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":612336,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":4491,\"account_id\":243,\"opportunity_id\":276,\"stage_id\":36}} {\"correlation_id\":\"1028998b-3369-4f2f-b7cb-e4ef79db71a5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612336} {\"correlation_id\":\"1028998b-3369-4f2f-b7cb-e4ef79db71a5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612336,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"1028998b-3369-4f2f-b7cb-e4ef79db71a5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":612336,\"participants\":[{\"id\":999508,\"user_id\":null,\"contact_id\":4491,\"lead_id\":null},{\"id\":999509,\"user_id\":206,\"contact_id\":null,\"lead_id\":null},{\"id\":999512,\"user_id\":null,\"contact_id\":null,\"lead_id\":null},{\"id\":999513,\"user_id\":null,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"1028998b-3369-4f2f-b7cb-e4ef79db71a5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"1028998b-3369-4f2f-b7cb-e4ef79db71a5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"1028998b-3369-4f2f-b7cb-e4ef79db71a5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"1028998b-3369-4f2f-b7cb-e4ef79db71a5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"1028998b-3369-4f2f-b7cb-e4ef79db71a5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"horencho@gmail.com\"} {\"correlation_id\":\"1028998b-3369-4f2f-b7cb-e4ef79db71a5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":612336,\"team_id\":2,\"email\":\"horen.kirazyan@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"1028998b-3369-4f2f-b7cb-e4ef79db71a5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":612336,\"participants_processed\":4,\"exact_matches\":1,\"domain_matches\":0,\"best_match_found\":true} {\"correlation_id\":\"1028998b-3369-4f2f-b7cb-e4ef79db71a5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612336} {\"correlation_id\":\"1028998b-3369-4f2f-b7cb-e4ef79db71a5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612336,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"1028998b-3369-4f2f-b7cb-e4ef79db71a5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":612336,\"remote_search\":true,\"lead_id\":null,\"contact_id\":4491,\"account_id\":243,\"opportunity_id\":276,\"stage_id\":36} {\"correlation_id\":\"1028998b-3369-4f2f-b7cb-e4ef79db71a5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":612183,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":4487,\"account_id\":244,\"opportunity_id\":299,\"stage_id\":36}} {\"correlation_id\":\"135ad495-9ac2-4ae2-9428-66f40f3a4731\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612183} {\"correlation_id\":\"135ad495-9ac2-4ae2-9428-66f40f3a4731\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612183,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"135ad495-9ac2-4ae2-9428-66f40f3a4731\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":612183,\"participants\":[{\"id\":999227,\"user_id\":null,\"contact_id\":4487,\"lead_id\":null},{\"id\":999228,\"user_id\":261,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"135ad495-9ac2-4ae2-9428-66f40f3a4731\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"135ad495-9ac2-4ae2-9428-66f40f3a4731\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"135ad495-9ac2-4ae2-9428-66f40f3a4731\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"135ad495-9ac2-4ae2-9428-66f40f3a4731\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"135ad495-9ac2-4ae2-9428-66f40f3a4731\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: ProspectCache - Searching DB for opportunity by owner {\"account_id\":244,\"contact_id\":4487,\"owner_id\":261} {\"correlation_id\":\"135ad495-9ac2-4ae2-9428-66f40f3a4731\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: ProspectCache - Opportunity DB search results {\"account_id\":244,\"contact_id\":4487,\"opportunity_id\":299} {\"correlation_id\":\"135ad495-9ac2-4ae2-9428-66f40f3a4731\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"robinson@crusoe.com\"} {\"correlation_id\":\"135ad495-9ac2-4ae2-9428-66f40f3a4731\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":612183,\"team_id\":2,\"email\":\"adelina.petrova@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"135ad495-9ac2-4ae2-9428-66f40f3a4731\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":612183,\"participants_processed\":2,\"exact_matches\":1,\"domain_matches\":0,\"best_match_found\":true} {\"correlation_id\":\"135ad495-9ac2-4ae2-9428-66f40f3a4731\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612183} {\"correlation_id\":\"135ad495-9ac2-4ae2-9428-66f40f3a4731\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612183,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"135ad495-9ac2-4ae2-9428-66f40f3a4731\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":612183,\"remote_search\":true,\"lead_id\":null,\"contact_id\":4487,\"account_id\":244,\"opportunity_id\":299,\"stage_id\":36} {\"correlation_id\":\"135ad495-9ac2-4ae2-9428-66f40f3a4731\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":612182,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":4487,\"account_id\":244,\"opportunity_id\":299,\"stage_id\":36}} {\"correlation_id\":\"9a2d51b0-64bc-41ea-86b1-8f3bcbb65a89\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612182} {\"correlation_id\":\"9a2d51b0-64bc-41ea-86b1-8f3bcbb65a89\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612182,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"9a2d51b0-64bc-41ea-86b1-8f3bcbb65a89\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":612182,\"participants\":[{\"id\":999225,\"user_id\":null,\"contact_id\":4487,\"lead_id\":null},{\"id\":999226,\"user_id\":261,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"9a2d51b0-64bc-41ea-86b1-8f3bcbb65a89\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"mailbox:batch:process\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.727} {\"correlation_id\":\"dceccba8-9e9c-476e-993d-809067884ebe\",\"trace_id\":\"66f8bba8-1712-464b-b25a-418f16736be2\"}\n[2026-05-11 11:06:17] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"9a2d51b0-64bc-41ea-86b1-8f3bcbb65a89\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"9a2d51b0-64bc-41ea-86b1-8f3bcbb65a89\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"9a2d51b0-64bc-41ea-86b1-8f3bcbb65a89\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"9a2d51b0-64bc-41ea-86b1-8f3bcbb65a89\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"robinson@crusoe.com\"} {\"correlation_id\":\"9a2d51b0-64bc-41ea-86b1-8f3bcbb65a89\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":612182,\"team_id\":2,\"email\":\"adelina.petrova@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"9a2d51b0-64bc-41ea-86b1-8f3bcbb65a89\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":612182,\"participants_processed\":2,\"exact_matches\":1,\"domain_matches\":0,\"best_match_found\":true} {\"correlation_id\":\"9a2d51b0-64bc-41ea-86b1-8f3bcbb65a89\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612182} {\"correlation_id\":\"9a2d51b0-64bc-41ea-86b1-8f3bcbb65a89\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612182,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"9a2d51b0-64bc-41ea-86b1-8f3bcbb65a89\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [EmailSchedule] STARTING batch process {\"host\":\"docker_lamp_1\"} {\"correlation_id\":\"dceccba8-9e9c-476e-993d-809067884ebe\",\"trace_id\":\"66f8bba8-1712-464b-b25a-418f16736be2\"}\n[2026-05-11 11:06:17] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":612182,\"remote_search\":true,\"lead_id\":null,\"contact_id\":4487,\"account_id\":244,\"opportunity_id\":299,\"stage_id\":36} {\"correlation_id\":\"9a2d51b0-64bc-41ea-86b1-8f3bcbb65a89\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [EmailSchedule] FINISHED batch process {\"host\":\"docker_lamp_1\",\"processed\":0} {\"correlation_id\":\"dceccba8-9e9c-476e-993d-809067884ebe\",\"trace_id\":\"66f8bba8-1712-464b-b25a-418f16736be2\"}\n[2026-05-11 11:06:17] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage for command {\"command\":\"mailbox:batch:process\",\"memoryBeforeCommandInMb\":60.0,\"memoryAfterCommandInMB\":60.0,\"memoryPeakBeforeCommandInMb\":99.727,\"memoryPeakAfterCommandInMB\":99.727} {\"correlation_id\":\"dceccba8-9e9c-476e-993d-809067884ebe\",\"trace_id\":\"66f8bba8-1712-464b-b25a-418f16736be2\"}\n[2026-05-11 11:06:17] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":612181,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":4487,\"account_id\":244,\"opportunity_id\":299,\"stage_id\":36}} {\"correlation_id\":\"8ff40f83-337f-432a-b950-e35779bb59bf\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612181} {\"correlation_id\":\"8ff40f83-337f-432a-b950-e35779bb59bf\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612181,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"8ff40f83-337f-432a-b950-e35779bb59bf\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":612181,\"participants\":[{\"id\":999223,\"user_id\":null,\"contact_id\":4487,\"lead_id\":null},{\"id\":999224,\"user_id\":261,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"8ff40f83-337f-432a-b950-e35779bb59bf\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"8ff40f83-337f-432a-b950-e35779bb59bf\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"8ff40f83-337f-432a-b950-e35779bb59bf\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"8ff40f83-337f-432a-b950-e35779bb59bf\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"8ff40f83-337f-432a-b950-e35779bb59bf\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"robinson@crusoe.com\"} {\"correlation_id\":\"8ff40f83-337f-432a-b950-e35779bb59bf\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":612181,\"team_id\":2,\"email\":\"adelina.petrova@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"8ff40f83-337f-432a-b950-e35779bb59bf\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":612181,\"participants_processed\":2,\"exact_matches\":1,\"domain_matches\":0,\"best_match_found\":true} {\"correlation_id\":\"8ff40f83-337f-432a-b950-e35779bb59bf\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612181} {\"correlation_id\":\"8ff40f83-337f-432a-b950-e35779bb59bf\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612181,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"8ff40f83-337f-432a-b950-e35779bb59bf\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":612181,\"remote_search\":true,\"lead_id\":null,\"contact_id\":4487,\"account_id\":244,\"opportunity_id\":299,\"stage_id\":36} {\"correlation_id\":\"8ff40f83-337f-432a-b950-e35779bb59bf\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":612180,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":4487,\"account_id\":244,\"opportunity_id\":299,\"stage_id\":36}} {\"correlation_id\":\"03f05b91-f803-45c9-b4bf-6a5f8ddf4a2c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612180} {\"correlation_id\":\"03f05b91-f803-45c9-b4bf-6a5f8ddf4a2c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612180,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"03f05b91-f803-45c9-b4bf-6a5f8ddf4a2c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":612180,\"participants\":[{\"id\":999221,\"user_id\":null,\"contact_id\":4487,\"lead_id\":null},{\"id\":999222,\"user_id\":261,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"03f05b91-f803-45c9-b4bf-6a5f8ddf4a2c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"03f05b91-f803-45c9-b4bf-6a5f8ddf4a2c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"03f05b91-f803-45c9-b4bf-6a5f8ddf4a2c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"03f05b91-f803-45c9-b4bf-6a5f8ddf4a2c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"03f05b91-f803-45c9-b4bf-6a5f8ddf4a2c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"robinson@crusoe.com\"} {\"correlation_id\":\"03f05b91-f803-45c9-b4bf-6a5f8ddf4a2c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":612180,\"team_id\":2,\"email\":\"adelina.petrova@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"03f05b91-f803-45c9-b4bf-6a5f8ddf4a2c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":612180,\"participants_processed\":2,\"exact_matches\":1,\"domain_matches\":0,\"best_match_found\":true} {\"correlation_id\":\"03f05b91-f803-45c9-b4bf-6a5f8ddf4a2c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612180} {\"correlation_id\":\"03f05b91-f803-45c9-b4bf-6a5f8ddf4a2c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612180,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"03f05b91-f803-45c9-b4bf-6a5f8ddf4a2c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":612180,\"remote_search\":true,\"lead_id\":null,\"contact_id\":4487,\"account_id\":244,\"opportunity_id\":299,\"stage_id\":36} {\"correlation_id\":\"03f05b91-f803-45c9-b4bf-6a5f8ddf4a2c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":611455,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89}} {\"correlation_id\":\"299c67e4-7026-46ee-b10d-d4e48fb55a7c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":611455} {\"correlation_id\":\"299c67e4-7026-46ee-b10d-d4e48fb55a7c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":611455,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"299c67e4-7026-46ee-b10d-d4e48fb55a7c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":611455,\"participants\":[{\"id\":997961,\"user_id\":null,\"contact_id\":null,\"lead_id\":null},{\"id\":997962,\"user_id\":1460,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"299c67e4-7026-46ee-b10d-d4e48fb55a7c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"299c67e4-7026-46ee-b10d-d4e48fb55a7c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"299c67e4-7026-46ee-b10d-d4e48fb55a7c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"299c67e4-7026-46ee-b10d-d4e48fb55a7c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"299c67e4-7026-46ee-b10d-d4e48fb55a7c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [Prospect match] Cache miss, calling the API {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"299c67e4-7026-46ee-b10d-d4e48fb55a7c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [Hubspot] Failed to fetch contact {\"email\":\"support@staging.jiminny.com\",\"reason\":\"[404] Client error: `GET https://api.hubapi.com/crm/v3/objects/contacts/support%40staging.jiminny.com?properties=email%2Cfirstname%2Clastname%2Ccountry%2Cphone%2Cmobilephone%2Cjobtitle%2Chubspot_owner_id%2Cassociatedcompanyid%2Cphoto&archived=0&idProperty=email` resulted in a `404 Not Found` response\"} {\"correlation_id\":\"299c67e4-7026-46ee-b10d-d4e48fb55a7c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [Prospect match] API returned empty result, caching the miss with empty prospect data {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"299c67e4-7026-46ee-b10d-d4e48fb55a7c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [Hubspot] Pagination completed {\"team_id\":2,\"endpoint\":\"https://api.hubapi.com/crm/v3/objects/contact/search\",\"total_requests\":1,\"total_records_fetched\":0,\"total_elapsed_seconds\":0.24,\"average_seconds_per_request\":0.24} {\"correlation_id\":\"299c67e4-7026-46ee-b10d-d4e48fb55a7c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [Prospect match] Resolved company domain from email {\"email\":\"support@staging.jiminny.com\",\"domain\":\"jiminny.com\"} {\"correlation_id\":\"299c67e4-7026-46ee-b10d-d4e48fb55a7c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [Prospect match] Cache miss {\"identifier_type\":\"domain\",\"identifier\":\"jiminny.com\",\"crm\":\"hubspot\"} {\"correlation_id\":\"299c67e4-7026-46ee-b10d-d4e48fb55a7c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [Prospect match] Cache miss, calling the API {\"identifier_type\":\"domain\",\"identifier\":\"jiminny.com\"} {\"correlation_id\":\"299c67e4-7026-46ee-b10d-d4e48fb55a7c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:19] local.INFO: [HubSpot] importAccount {\"crm_provider_id\":\"749766179\",\"config_id\":2} {\"correlation_id\":\"299c67e4-7026-46ee-b10d-d4e48fb55a7c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:19] local.INFO: [HubSpot] CRM Search requested {\"request\":{\"filterGroups\":[{\"filters\":[{\"propertyName\":\"associations.company\",\"operator\":\"EQ\",\"value\":\"749766179\"},{\"propertyName\":\"dealstage\",\"operator\":\"NOT_IN\",\"values\":[\"closedwon\",\"4040964\",\"59247967\"]},{\"propertyName\":\"dealstage\",\"operator\":\"NOT_IN\",\"values\":[\"closedlost\",\"4040965\",\"59247968\"]}]}],\"sorts\":[{\"propertyName\":\"modifieddate\",\"direction\":\"DESCENDING\"}],\"properties\":[\"dealname\",\"amount\",\"hubspot_owner_id\",\"pipeline\",\"dealstage\",\"closedate\",\"deal_currency_code\"],\"limit\":200}} {\"correlation_id\":\"299c67e4-7026-46ee-b10d-d4e48fb55a7c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:19] local.INFO: [Hubspot] Pagination completed {\"team_id\":2,\"endpoint\":\"https://api.hubapi.com/crm/v3/objects/deals/search\",\"total_requests\":1,\"total_records_fetched\":10,\"total_elapsed_seconds\":0.34,\"average_seconds_per_request\":0.34} {\"correlation_id\":\"299c67e4-7026-46ee-b10d-d4e48fb55a7c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:20] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":611455,\"team_id\":2,\"email\":\"aneliya.angelova@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"299c67e4-7026-46ee-b10d-d4e48fb55a7c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:21] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":611455,\"participants_processed\":2,\"exact_matches\":0,\"domain_matches\":1,\"best_match_found\":true} {\"correlation_id\":\"299c67e4-7026-46ee-b10d-d4e48fb55a7c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:21] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":611455} {\"correlation_id\":\"299c67e4-7026-46ee-b10d-d4e48fb55a7c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:21] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":611455,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"299c67e4-7026-46ee-b10d-d4e48fb55a7c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:21] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":611455,\"remote_search\":true,\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89} {\"correlation_id\":\"299c67e4-7026-46ee-b10d-d4e48fb55a7c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:21] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":611451,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89}} {\"correlation_id\":\"2385adb3-c893-49f8-9fbc-65b354009bea\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:21] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":611451} {\"correlation_id\":\"2385adb3-c893-49f8-9fbc-65b354009bea\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:21] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":611451,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"2385adb3-c893-49f8-9fbc-65b354009bea\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:21] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":611451,\"participants\":[{\"id\":997955,\"user_id\":null,\"contact_id\":null,\"lead_id\":null},{\"id\":997956,\"user_id\":18,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"2385adb3-c893-49f8-9fbc-65b354009bea\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:21] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"2385adb3-c893-49f8-9fbc-65b354009bea\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:21] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"2385adb3-c893-49f8-9fbc-65b354009bea\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:21] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"2385adb3-c893-49f8-9fbc-65b354009bea\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:21] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"2385adb3-c893-49f8-9fbc-65b354009bea\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:21] local.INFO: [Prospect match] Cache miss, calling the API {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"2385adb3-c893-49f8-9fbc-65b354009bea\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:23] local.INFO: [Hubspot] Failed to fetch contact {\"email\":\"support@staging.jiminny.com\",\"reason\":\"[404] Client error: `GET https://api.hubapi.com/crm/v3/objects/contacts/support%40staging.jiminny.com?properties=email%2Cfirstname%2Clastname%2Ccountry%2Cphone%2Cmobilephone%2Cjobtitle%2Chubspot_owner_id%2Cassociatedcompanyid%2Cphoto&archived=0&idProperty=email` resulted in a `404 Not Found` response\"} {\"correlation_id\":\"2385adb3-c893-49f8-9fbc-65b354009bea\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:23] local.INFO: [Prospect match] API returned empty result, caching the miss with empty prospect data {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"2385adb3-c893-49f8-9fbc-65b354009bea\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:23] local.INFO: [Hubspot] Pagination completed {\"team_id\":2,\"endpoint\":\"https://api.hubapi.com/crm/v3/objects/contact/search\",\"total_requests\":1,\"total_records_fetched\":0,\"total_elapsed_seconds\":0.49,\"average_seconds_per_request\":0.49} {\"correlation_id\":\"2385adb3-c893-49f8-9fbc-65b354009bea\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:24] local.INFO: [Prospect match] Resolved company domain from email {\"email\":\"support@staging.jiminny.com\",\"domain\":\"jiminny.com\"} {\"correlation_id\":\"2385adb3-c893-49f8-9fbc-65b354009bea\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:24] local.INFO: [Prospect match] Cache miss {\"identifier_type\":\"domain\",\"identifier\":\"jiminny.com\",\"crm\":\"hubspot\"} {\"correlation_id\":\"2385adb3-c893-49f8-9fbc-65b354009bea\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:24] local.INFO: [Prospect match] Cache miss, calling the API {\"identifier_type\":\"domain\",\"identifier\":\"jiminny.com\"} {\"correlation_id\":\"2385adb3-c893-49f8-9fbc-65b354009bea\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:24] local.INFO: [HubSpot] importAccount {\"crm_provider_id\":\"749766179\",\"config_id\":2} {\"correlation_id\":\"2385adb3-c893-49f8-9fbc-65b354009bea\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:24] local.INFO: [HubSpot] CRM Search requested {\"request\":{\"filterGroups\":[{\"filters\":[{\"propertyName\":\"associations.company\",\"operator\":\"EQ\",\"value\":\"749766179\"},{\"propertyName\":\"dealstage\",\"operator\":\"NOT_IN\",\"values\":[\"closedwon\",\"4040964\",\"59247967\"]},{\"propertyName\":\"dealstage\",\"operator\":\"NOT_IN\",\"values\":[\"closedlost\",\"4040965\",\"59247968\"]}]}],\"sorts\":[{\"propertyName\":\"modifieddate\",\"direction\":\"DESCENDING\"}],\"properties\":[\"dealname\",\"amount\",\"hubspot_owner_id\",\"pipeline\",\"dealstage\",\"closedate\",\"deal_currency_code\"],\"limit\":200}} {\"correlation_id\":\"2385adb3-c893-49f8-9fbc-65b354009bea\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:24] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"activity:purge-stale\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.727} {\"correlation_id\":\"1b5b216e-e0a9-4898-9f26-180cd26a0438\",\"trace_id\":\"f8d4ad10-fc79-469f-a5b8-e89d8fe8c392\"}\n[2026-05-11 11:06:24] local.INFO: [ EsUpdateProcessManager ] Finished updating entities in ES {\"worker\":\"\",\"peak_memory\":\"99.73 MB\",\"elapsed_seconds\":3.18,\"update_target\":\"activities\",\"should_iterate_again\":false} {\"correlation_id\":\"38935ae1-fcb3-437e-9c9e-1b7415a0b7e1\",\"trace_id\":\"325a2f9b-9c66-4223-ac02-1a43723f4315\"}\n[2026-05-11 11:06:24] local.INFO: [Hubspot] Pagination completed {\"team_id\":2,\"endpoint\":\"https://api.hubapi.com/crm/v3/objects/deals/search\",\"total_requests\":1,\"total_records_fetched\":10,\"total_elapsed_seconds\":0.26,\"average_seconds_per_request\":0.26} {\"correlation_id\":\"2385adb3-c893-49f8-9fbc-65b354009bea\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:25] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage for command {\"command\":\"activity:purge-stale\",\"memoryBeforeCommandInMb\":60.0,\"memoryAfterCommandInMB\":60.0,\"memoryPeakBeforeCommandInMb\":99.727,\"memoryPeakAfterCommandInMB\":99.727} {\"correlation_id\":\"1b5b216e-e0a9-4898-9f26-180cd26a0438\",\"trace_id\":\"f8d4ad10-fc79-469f-a5b8-e89d8fe8c392\"}\n[2026-05-11 11:06:25] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":611451,\"team_id\":2,\"email\":\"veselin.kulov@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"2385adb3-c893-49f8-9fbc-65b354009bea\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:25] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":611451,\"participants_processed\":2,\"exact_matches\":0,\"domain_matches\":1,\"best_match_found\":true} {\"correlation_id\":\"2385adb3-c893-49f8-9fbc-65b354009bea\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:25] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":611451} {\"correlation_id\":\"2385adb3-c893-49f8-9fbc-65b354009bea\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:25] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":611451,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"2385adb3-c893-49f8-9fbc-65b354009bea\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:25] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":611451,\"remote_search\":true,\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89} {\"correlation_id\":\"2385adb3-c893-49f8-9fbc-65b354009bea\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":611087,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89}} {\"correlation_id\":\"a6d1c39e-8bb7-43da-81ba-7a22dc8de463\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":611087} {\"correlation_id\":\"a6d1c39e-8bb7-43da-81ba-7a22dc8de463\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":611087,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"a6d1c39e-8bb7-43da-81ba-7a22dc8de463\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":611087,\"participants\":[{\"id\":997368,\"user_id\":null,\"contact_id\":null,\"lead_id\":null},{\"id\":997369,\"user_id\":18,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"a6d1c39e-8bb7-43da-81ba-7a22dc8de463\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"a6d1c39e-8bb7-43da-81ba-7a22dc8de463\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"a6d1c39e-8bb7-43da-81ba-7a22dc8de463\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"a6d1c39e-8bb7-43da-81ba-7a22dc8de463\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"a6d1c39e-8bb7-43da-81ba-7a22dc8de463\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"a6d1c39e-8bb7-43da-81ba-7a22dc8de463\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [Prospect match] cached empty result - no API calls, try next matching method {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"a6d1c39e-8bb7-43da-81ba-7a22dc8de463\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [Prospect match] Resolved company domain from email {\"email\":\"support@staging.jiminny.com\",\"domain\":\"jiminny.com\"} {\"correlation_id\":\"a6d1c39e-8bb7-43da-81ba-7a22dc8de463\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":611087,\"team_id\":2,\"email\":\"veselin.kulov@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"a6d1c39e-8bb7-43da-81ba-7a22dc8de463\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":611087,\"participants_processed\":2,\"exact_matches\":0,\"domain_matches\":1,\"best_match_found\":true} {\"correlation_id\":\"a6d1c39e-8bb7-43da-81ba-7a22dc8de463\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":611087} {\"correlation_id\":\"a6d1c39e-8bb7-43da-81ba-7a22dc8de463\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":611087,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"a6d1c39e-8bb7-43da-81ba-7a22dc8de463\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":611087,\"remote_search\":true,\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89} {\"correlation_id\":\"a6d1c39e-8bb7-43da-81ba-7a22dc8de463\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":611076,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89}} {\"correlation_id\":\"8905b485-a3d2-474a-a2dd-dd5375deb178\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":611076} {\"correlation_id\":\"8905b485-a3d2-474a-a2dd-dd5375deb178\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":611076,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"8905b485-a3d2-474a-a2dd-dd5375deb178\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":611076,\"participants\":[{\"id\":997346,\"user_id\":null,\"contact_id\":null,\"lead_id\":null},{\"id\":997347,\"user_id\":18,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"8905b485-a3d2-474a-a2dd-dd5375deb178\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"8905b485-a3d2-474a-a2dd-dd5375deb178\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"8905b485-a3d2-474a-a2dd-dd5375deb178\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"8905b485-a3d2-474a-a2dd-dd5375deb178\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"8905b485-a3d2-474a-a2dd-dd5375deb178\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"8905b485-a3d2-474a-a2dd-dd5375deb178\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [Prospect match] cached empty result - no API calls, try next matching method {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"8905b485-a3d2-474a-a2dd-dd5375deb178\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [Prospect match] Resolved company domain from email {\"email\":\"support@staging.jiminny.com\",\"domain\":\"jiminny.com\"} {\"correlation_id\":\"8905b485-a3d2-474a-a2dd-dd5375deb178\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":611076,\"team_id\":2,\"email\":\"veselin.kulov@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"8905b485-a3d2-474a-a2dd-dd5375deb178\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":611076,\"participants_processed\":2,\"exact_matches\":0,\"domain_matches\":1,\"best_match_found\":true} {\"correlation_id\":\"8905b485-a3d2-474a-a2dd-dd5375deb178\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":611076} {\"correlation_id\":\"8905b485-a3d2-474a-a2dd-dd5375deb178\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":611076,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"8905b485-a3d2-474a-a2dd-dd5375deb178\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":611076,\"remote_search\":true,\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89} {\"correlation_id\":\"8905b485-a3d2-474a-a2dd-dd5375deb178\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":610935,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89}} {\"correlation_id\":\"77fcbcd0-06e6-4680-85f5-cdefdd76007c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610935} {\"correlation_id\":\"77fcbcd0-06e6-4680-85f5-cdefdd76007c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610935,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"77fcbcd0-06e6-4680-85f5-cdefdd76007c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":610935,\"participants\":[{\"id\":997141,\"user_id\":null,\"contact_id\":null,\"lead_id\":null},{\"id\":997142,\"user_id\":18,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"77fcbcd0-06e6-4680-85f5-cdefdd76007c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"77fcbcd0-06e6-4680-85f5-cdefdd76007c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"77fcbcd0-06e6-4680-85f5-cdefdd76007c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"77fcbcd0-06e6-4680-85f5-cdefdd76007c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"77fcbcd0-06e6-4680-85f5-cdefdd76007c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"77fcbcd0-06e6-4680-85f5-cdefdd76007c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [Prospect match] cached empty result - no API calls, try next matching method {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"77fcbcd0-06e6-4680-85f5-cdefdd76007c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [Prospect match] Resolved company domain from email {\"email\":\"support@staging.jiminny.com\",\"domain\":\"jiminny.com\"} {\"correlation_id\":\"77fcbcd0-06e6-4680-85f5-cdefdd76007c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":610935,\"team_id\":2,\"email\":\"veselin.kulov@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"77fcbcd0-06e6-4680-85f5-cdefdd76007c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":610935,\"participants_processed\":2,\"exact_matches\":0,\"domain_matches\":1,\"best_match_found\":true} {\"correlation_id\":\"77fcbcd0-06e6-4680-85f5-cdefdd76007c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610935} {\"correlation_id\":\"77fcbcd0-06e6-4680-85f5-cdefdd76007c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610935,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"77fcbcd0-06e6-4680-85f5-cdefdd76007c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":610935,\"remote_search\":true,\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89} {\"correlation_id\":\"77fcbcd0-06e6-4680-85f5-cdefdd76007c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":610915,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89}} {\"correlation_id\":\"79639904-266b-4873-8940-014db94b5d8f\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610915} {\"correlation_id\":\"79639904-266b-4873-8940-014db94b5d8f\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610915,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"79639904-266b-4873-8940-014db94b5d8f\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":610915,\"participants\":[{\"id\":997104,\"user_id\":null,\"contact_id\":null,\"lead_id\":null},{\"id\":997105,\"user_id\":18,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"79639904-266b-4873-8940-014db94b5d8f\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"79639904-266b-4873-8940-014db94b5d8f\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"79639904-266b-4873-8940-014db94b5d8f\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"79639904-266b-4873-8940-014db94b5d8f\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"79639904-266b-4873-8940-014db94b5d8f\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"79639904-266b-4873-8940-014db94b5d8f\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [Prospect match] cached empty result - no API calls, try next matching method {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"79639904-266b-4873-8940-014db94b5d8f\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [Prospect match] Resolved company domain from email {\"email\":\"support@staging.jiminny.com\",\"domain\":\"jiminny.com\"} {\"correlation_id\":\"79639904-266b-4873-8940-014db94b5d8f\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":610915,\"team_id\":2,\"email\":\"veselin.kulov@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"79639904-266b-4873-8940-014db94b5d8f\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":610915,\"participants_processed\":2,\"exact_matches\":0,\"domain_matches\":1,\"best_match_found\":true} {\"correlation_id\":\"79639904-266b-4873-8940-014db94b5d8f\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610915} {\"correlation_id\":\"79639904-266b-4873-8940-014db94b5d8f\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610915,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"79639904-266b-4873-8940-014db94b5d8f\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":610915,\"remote_search\":true,\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89} {\"correlation_id\":\"79639904-266b-4873-8940-014db94b5d8f\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":610900,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89}} {\"correlation_id\":\"068ab494-a35a-4562-b447-44bf2ddb775d\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610900} {\"correlation_id\":\"068ab494-a35a-4562-b447-44bf2ddb775d\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610900,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"068ab494-a35a-4562-b447-44bf2ddb775d\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":610900,\"participants\":[{\"id\":997081,\"user_id\":null,\"contact_id\":null,\"lead_id\":null},{\"id\":997082,\"user_id\":18,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"068ab494-a35a-4562-b447-44bf2ddb775d\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"068ab494-a35a-4562-b447-44bf2ddb775d\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"068ab494-a35a-4562-b447-44bf2ddb775d\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"068ab494-a35a-4562-b447-44bf2ddb775d\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"068ab494-a35a-4562-b447-44bf2ddb775d\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"068ab494-a35a-4562-b447-44bf2ddb775d\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [Prospect match] cached empty result - no API calls, try next matching method {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"068ab494-a35a-4562-b447-44bf2ddb775d\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [Prospect match] Resolved company domain from email {\"email\":\"support@staging.jiminny.com\",\"domain\":\"jiminny.com\"} {\"correlation_id\":\"068ab494-a35a-4562-b447-44bf2ddb775d\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":610900,\"team_id\":2,\"email\":\"veselin.kulov@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"068ab494-a35a-4562-b447-44bf2ddb775d\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":610900,\"participants_processed\":2,\"exact_matches\":0,\"domain_matches\":1,\"best_match_found\":true} {\"correlation_id\":\"068ab494-a35a-4562-b447-44bf2ddb775d\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610900} {\"correlation_id\":\"068ab494-a35a-4562-b447-44bf2ddb775d\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610900,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"068ab494-a35a-4562-b447-44bf2ddb775d\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":610900,\"remote_search\":true,\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89} {\"correlation_id\":\"068ab494-a35a-4562-b447-44bf2ddb775d\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":610885,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89}} {\"correlation_id\":\"a556c8c6-d98c-4c17-b45c-e5ba57a68062\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610885} {\"correlation_id\":\"a556c8c6-d98c-4c17-b45c-e5ba57a68062\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610885,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"a556c8c6-d98c-4c17-b45c-e5ba57a68062\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":610885,\"participants\":[{\"id\":997051,\"user_id\":null,\"contact_id\":null,\"lead_id\":null},{\"id\":997052,\"user_id\":18,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"a556c8c6-d98c-4c17-b45c-e5ba57a68062\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"a556c8c6-d98c-4c17-b45c-e5ba57a68062\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"a556c8c6-d98c-4c17-b45c-e5ba57a68062\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"a556c8c6-d98c-4c17-b45c-e5ba57a68062\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"a556c8c6-d98c-4c17-b45c-e5ba57a68062\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"a556c8c6-d98c-4c17-b45c-e5ba57a68062\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [Prospect match] cached empty result - no API calls, try next matching method {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"a556c8c6-d98c-4c17-b45c-e5ba57a68062\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [Prospect match] Resolved company domain from email {\"email\":\"support@staging.jiminny.com\",\"domain\":\"jiminny.com\"} {\"correlation_id\":\"a556c8c6-d98c-4c17-b45c-e5ba57a68062\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":610885,\"team_id\":2,\"email\":\"veselin.kulov@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"a556c8c6-d98c-4c17-b45c-e5ba57a68062\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":610885,\"participants_processed\":2,\"exact_matches\":0,\"domain_matches\":1,\"best_match_found\":true} {\"correlation_id\":\"a556c8c6-d98c-4c17-b45c-e5ba57a68062\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610885} {\"correlation_id\":\"a556c8c6-d98c-4c17-b45c-e5ba57a68062\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610885,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"a556c8c6-d98c-4c17-b45c-e5ba57a68062\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":610885,\"remote_search\":true,\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89} {\"correlation_id\":\"a556c8c6-d98c-4c17-b45c-e5ba57a68062\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":610878,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89}} {\"correlation_id\":\"915d4deb-56d3-4de7-bc74-2274b327505c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610878} {\"correlation_id\":\"915d4deb-56d3-4de7-bc74-2274b327505c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610878,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"915d4deb-56d3-4de7-bc74-2274b327505c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":610878,\"participants\":[{\"id\":997035,\"user_id\":null,\"contact_id\":null,\"lead_id\":null},{\"id\":997036,\"user_id\":18,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"915d4deb-56d3-4de7-bc74-2274b327505c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"915d4deb-56d3-4de7-bc74-2274b327505c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"915d4deb-56d3-4de7-bc74-2274b327505c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"915d4deb-56d3-4de7-bc74-2274b327505c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"915d4deb-56d3-4de7-bc74-2274b327505c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"915d4deb-56d3-4de7-bc74-2274b327505c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [Prospect match] cached empty result - no API calls, try next matching method {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"915d4deb-56d3-4de7-bc74-2274b327505c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [Prospect match] Resolved company domain from email {\"email\":\"support@staging.jiminny.com\",\"domain\":\"jiminny.com\"} {\"correlation_id\":\"915d4deb-56d3-4de7-bc74-2274b327505c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":610878,\"team_id\":2,\"email\":\"veselin.kulov@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"915d4deb-56d3-4de7-bc74-2274b327505c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":610878,\"participants_processed\":2,\"exact_matches\":0,\"domain_matches\":1,\"best_match_found\":true} {\"correlation_id\":\"915d4deb-56d3-4de7-bc74-2274b327505c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610878} {\"correlation_id\":\"915d4deb-56d3-4de7-bc74-2274b327505c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610878,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"915d4deb-56d3-4de7-bc74-2274b327505c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":610878,\"remote_search\":true,\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89} {\"correlation_id\":\"915d4deb-56d3-4de7-bc74-2274b327505c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":610874,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89}} {\"correlation_id\":\"1b89c98c-65d3-4286-b140-c9453bca9a74\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610874} {\"correlation_id\":\"1b89c98c-65d3-4286-b140-c9453bca9a74\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610874,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"1b89c98c-65d3-4286-b140-c9453bca9a74\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":610874,\"participants\":[{\"id\":997025,\"user_id\":null,\"contact_id\":null,\"lead_id\":null},{\"id\":997026,\"user_id\":18,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"1b89c98c-65d3-4286-b140-c9453bca9a74\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"1b89c98c-65d3-4286-b140-c9453bca9a74\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"1b89c98c-65d3-4286-b140-c9453bca9a74\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"1b89c98c-65d3-4286-b140-c9453bca9a74\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"1b89c98c-65d3-4286-b140-c9453bca9a74\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"1b89c98c-65d3-4286-b140-c9453bca9a74\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [Prospect match] cached empty result - no API calls, try next matching method {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"1b89c98c-65d3-4286-b140-c9453bca9a74\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [Prospect match] Resolved company domain from email {\"email\":\"support@staging.jiminny.com\",\"domain\":\"jiminny.com\"} {\"correlation_id\":\"1b89c98c-65d3-4286-b140-c9453bca9a74\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":610874,\"team_id\":2,\"email\":\"veselin.kulov@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"1b89c98c-65d3-4286-b140-c9453bca9a74\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":610874,\"participants_processed\":2,\"exact_matches\":0,\"domain_matches\":1,\"best_match_found\":true} {\"correlation_id\":\"1b89c98c-65d3-4286-b140-c9453bca9a74\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610874} {\"correlation_id\":\"1b89c98c-65d3-4286-b140-c9453bca9a74\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610874,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"1b89c98c-65d3-4286-b140-c9453bca9a74\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":610874,\"remote_search\":true,\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89} {\"correlation_id\":\"1b89c98c-65d3-4286-b140-c9453bca9a74\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":610867,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89}} {\"correlation_id\":\"8a33657f-373a-4b5d-b1f9-71bbe85bd3ba\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610867} {\"correlation_id\":\"8a33657f-373a-4b5d-b1f9-71bbe85bd3ba\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610867,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"8a33657f-373a-4b5d-b1f9-71bbe85bd3ba\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":610867,\"participants\":[{\"id\":997011,\"user_id\":null,\"contact_id\":null,\"lead_id\":null},{\"id\":997012,\"user_id\":18,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"8a33657f-373a-4b5d-b1f9-71bbe85bd3ba\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"8a33657f-373a-4b5d-b1f9-71bbe85bd3ba\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"8a33657f-373a-4b5d-b1f9-71bbe85bd3ba\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"8a33657f-373a-4b5d-b1f9-71bbe85bd3ba\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"8a33657f-373a-4b5d-b1f9-71bbe85bd3ba\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"8a33657f-373a-4b5d-b1f9-71bbe85bd3ba\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [Prospect match] cached empty result - no API calls, try next matching method {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"8a33657f-373a-4b5d-b1f9-71bbe85bd3ba\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [Prospect match] Resolved company domain from email {\"email\":\"support@staging.jiminny.com\",\"domain\":\"jiminny.com\"} {\"correlation_id\":\"8a33657f-373a-4b5d-b1f9-71bbe85bd3ba\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":610867,\"team_id\":2,\"email\":\"veselin.kulov@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"8a33657f-373a-4b5d-b1f9-71bbe85bd3ba\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":610867,\"participants_processed\":2,\"exact_matches\":0,\"domain_matches\":1,\"best_match_found\":true} {\"correlation_id\":\"8a33657f-373a-4b5d-b1f9-71bbe85bd3ba\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610867} {\"correlation_id\":\"8a33657f-373a-4b5d-b1f9-71bbe85bd3ba\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610867,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"8a33657f-373a-4b5d-b1f9-71bbe85bd3ba\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":610867,\"remote_search\":true,\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89} {\"correlation_id\":\"8a33657f-373a-4b5d-b1f9-71bbe85bd3ba\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":610764,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89}} {\"correlation_id\":\"440ce0fb-99c1-458c-9db4-c11e504dd620\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610764} {\"correlation_id\":\"440ce0fb-99c1-458c-9db4-c11e504dd620\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610764,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"440ce0fb-99c1-458c-9db4-c11e504dd620\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":610764,\"participants\":[{\"id\":996951,\"user_id\":null,\"contact_id\":null,\"lead_id\":null},{\"id\":996952,\"user_id\":18,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"440ce0fb-99c1-458c-9db4-c11e504dd620\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"440ce0fb-99c1-458c-9db4-c11e504dd620\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"440ce0fb-99c1-458c-9db4-c11e504dd620\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"440ce0fb-99c1-458c-9db4-c11e504dd620\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"440ce0fb-99c1-458c-9db4-c11e504dd620\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"440ce0fb-99c1-458c-9db4-c11e504dd620\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [Prospect match] cached empty result - no API calls, try next matching method {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"440ce0fb-99c1-458c-9db4-c11e504dd620\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [Prospect match] Resolved company domain from email {\"email\":\"support@staging.jiminny.com\",\"domain\":\"jiminny.com\"} {\"correlation_id\":\"440ce0fb-99c1-458c-9db4-c11e504dd620\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":610764,\"team_id\":2,\"email\":\"veselin.kulov@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"440ce0fb-99c1-458c-9db4-c11e504dd620\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":610764,\"participants_processed\":2,\"exact_matches\":0,\"domain_matches\":1,\"best_match_found\":true} {\"correlation_id\":\"440ce0fb-99c1-458c-9db4-c11e504dd620\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610764} {\"correlation_id\":\"440ce0fb-99c1-458c-9db4-c11e504dd620\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610764,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"440ce0fb-99c1-458c-9db4-c11e504dd620\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":610764,\"remote_search\":true,\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89} {\"correlation_id\":\"440ce0fb-99c1-458c-9db4-c11e504dd620\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":610617,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89}} {\"correlation_id\":\"d8853964-8fe9-4553-96da-d088b6dfc843\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610617} {\"correlation_id\":\"d8853964-8fe9-4553-96da-d088b6dfc843\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610617,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"d8853964-8fe9-4553-96da-d088b6dfc843\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":610617,\"participants\":[{\"id\":996641,\"user_id\":null,\"contact_id\":null,\"lead_id\":null},{\"id\":996642,\"user_id\":18,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"d8853964-8fe9-4553-96da-d088b6dfc843\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"d8853964-8fe9-4553-96da-d088b6dfc843\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"d8853964-8fe9-4553-96da-d088b6dfc843\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"d8853964-8fe9-4553-96da-d088b6dfc843\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"d8853964-8fe9-4553-96da-d088b6dfc843\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"d8853964-8fe9-4553-96da-d088b6dfc843\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [Prospect match] cached empty result - no API calls, try next matching method {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"d8853964-8fe9-4553-96da-d088b6dfc843\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [Prospect match] Resolved company domain from email {\"email\":\"support@staging.jiminny.com\",\"domain\":\"jiminny.com\"} {\"correlation_id\":\"d8853964-8fe9-4553-96da-d088b6dfc843\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":610617,\"team_id\":2,\"email\":\"veselin.kulov@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"d8853964-8fe9-4553-96da-d088b6dfc843\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":610617,\"participants_processed\":2,\"exact_matches\":0,\"domain_matches\":1,\"best_match_found\":true} {\"correlation_id\":\"d8853964-8fe9-4553-96da-d088b6dfc843\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610617} {\"correlation_id\":\"d8853964-8fe9-4553-96da-d088b6dfc843\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610617,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"d8853964-8fe9-4553-96da-d088b6dfc843\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":610617,\"remote_search\":true,\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89} {\"correlation_id\":\"d8853964-8fe9-4553-96da-d088b6dfc843\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":610539,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89}} {\"correlation_id\":\"d021767c-298a-4ad0-9f72-dca085cc6528\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610539} {\"correlation_id\":\"d021767c-298a-4ad0-9f72-dca085cc6528\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610539,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"d021767c-298a-4ad0-9f72-dca085cc6528\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":610539,\"participants\":[{\"id\":996485,\"user_id\":null,\"contact_id\":null,\"lead_id\":null},{\"id\":996486,\"user_id\":18,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"d021767c-298a-4ad0-9f72-dca085cc6528\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"d021767c-298a-4ad0-9f72-dca085cc6528\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"d021767c-298a-4ad0-9f72-dca085cc6528\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"d021767c-298a-4ad0-9f72-dca085cc6528\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"d021767c-298a-4ad0-9f72-dca085cc6528\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"d021767c-298a-4ad0-9f72-dca085cc6528\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [Prospect match] cached empty result - no API calls, try next matching method {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"d021767c-298a-4ad0-9f72-dca085cc6528\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [Prospect match] Resolved company domain from email {\"email\":\"support@staging.jiminny.com\",\"domain\":\"jiminny.com\"} {\"correlation_id\":\"d021767c-298a-4ad0-9f72-dca085cc6528\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":610539,\"team_id\":2,\"email\":\"veselin.kulov@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"d021767c-298a-4ad0-9f72-dca085cc6528\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":610539,\"participants_processed\":2,\"exact_matches\":0,\"domain_matches\":1,\"best_match_found\":true} {\"correlation_id\":\"d021767c-298a-4ad0-9f72-dca085cc6528\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610539} {\"correlation_id\":\"d021767c-298a-4ad0-9f72-dca085cc6528\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610539,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"d021767c-298a-4ad0-9f72-dca085cc6528\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"mailbox:text-relay:sync\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.727} {\"correlation_id\":\"7c0c4db8-2e4e-4aa1-aa71-a3d63cdb81d7\",\"trace_id\":\"b3e9e2f5-3256-429a-ad76-e46e3c2e626d\"}\n[2026-05-11 11:06:28] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":610539,\"remote_search\":true,\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89} {\"correlation_id\":\"d021767c-298a-4ad0-9f72-dca085cc6528\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":610528,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89}} {\"correlation_id\":\"2410a4b8-a997-4cf1-99f0-c23ba54805c7\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610528} {\"correlation_id\":\"2410a4b8-a997-4cf1-99f0-c23ba54805c7\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610528,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"2410a4b8-a997-4cf1-99f0-c23ba54805c7\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":610528,\"participants\":[{\"id\":996463,\"user_id\":null,\"contact_id\":null,\"lead_id\":null},{\"id\":996464,\"user_id\":18,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"2410a4b8-a997-4cf1-99f0-c23ba54805c7\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"2410a4b8-a997-4cf1-99f0-c23ba54805c7\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"2410a4b8-a997-4cf1-99f0-c23ba54805c7\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"2410a4b8-a997-4cf1-99f0-c23ba54805c7\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"2410a4b8-a997-4cf1-99f0-c23ba54805c7\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"2410a4b8-a997-4cf1-99f0-c23ba54805c7\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [Prospect match] cached empty result - no API calls, try next matching method {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"2410a4b8-a997-4cf1-99f0-c23ba54805c7\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [Prospect match] Resolved company domain from email {\"email\":\"support@staging.jiminny.com\",\"domain\":\"jiminny.com\"} {\"correlation_id\":\"2410a4b8-a997-4cf1-99f0-c23ba54805c7\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":610528,\"team_id\":2,\"email\":\"veselin.kulov@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"2410a4b8-a997-4cf1-99f0-c23ba54805c7\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":610528,\"participants_processed\":2,\"exact_matches\":0,\"domain_matches\":1,\"best_match_found\":true} {\"correlation_id\":\"2410a4b8-a997-4cf1-99f0-c23ba54805c7\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610528} {\"correlation_id\":\"2410a4b8-a997-4cf1-99f0-c23ba54805c7\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610528,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"2410a4b8-a997-4cf1-99f0-c23ba54805c7\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":610528,\"remote_search\":true,\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89} {\"correlation_id\":\"2410a4b8-a997-4cf1-99f0-c23ba54805c7\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":610506,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89}} {\"correlation_id\":\"8b5bce8c-7e89-46ba-b04b-9b9ab4e620b6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610506} {\"correlation_id\":\"8b5bce8c-7e89-46ba-b04b-9b9ab4e620b6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610506,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"8b5bce8c-7e89-46ba-b04b-9b9ab4e620b6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":610506,\"participants\":[{\"id\":996419,\"user_id\":null,\"contact_id\":null,\"lead_id\":null},{\"id\":996420,\"user_id\":18,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"8b5bce8c-7e89-46ba-b04b-9b9ab4e620b6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"8b5bce8c-7e89-46ba-b04b-9b9ab4e620b6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"8b5bce8c-7e89-46ba-b04b-9b9ab4e620b6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"8b5bce8c-7e89-46ba-b04b-9b9ab4e620b6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"8b5bce8c-7e89-46ba-b04b-9b9ab4e620b6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"8b5bce8c-7e89-46ba-b04b-9b9ab4e620b6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [Prospect match] cached empty result - no API calls, try next matching method {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"8b5bce8c-7e89-46ba-b04b-9b9ab4e620b6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [Prospect match] Resolved company domain from email {\"email\":\"support@staging.jiminny.com\",\"domain\":\"jiminny.com\"} {\"correlation_id\":\"8b5bce8c-7e89-46ba-b04b-9b9ab4e620b6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":610506,\"team_id\":2,\"email\":\"veselin.kulov@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"8b5bce8c-7e89-46ba-b04b-9b9ab4e620b6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":610506,\"participants_processed\":2,\"exact_matches\":0,\"domain_matches\":1,\"best_match_found\":true} {\"correlation_id\":\"8b5bce8c-7e89-46ba-b04b-9b9ab4e620b6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610506} {\"correlation_id\":\"8b5bce8c-7e89-46ba-b04b-9b9ab4e620b6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610506,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"8b5bce8c-7e89-46ba-b04b-9b9ab4e620b6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":610506,\"remote_search\":true,\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89} {\"correlation_id\":\"8b5bce8c-7e89-46ba-b04b-9b9ab4e620b6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":610497,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89}} {\"correlation_id\":\"a9e55e87-8951-406b-84a8-6fecf2841584\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610497} {\"correlation_id\":\"a9e55e87-8951-406b-84a8-6fecf2841584\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610497,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"a9e55e87-8951-406b-84a8-6fecf2841584\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":610497,\"participants\":[{\"id\":996401,\"user_id\":null,\"contact_id\":null,\"lead_id\":null},{\"id\":996402,\"user_id\":18,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"a9e55e87-8951-406b-84a8-6fecf2841584\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"a9e55e87-8951-406b-84a8-6fecf2841584\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"a9e55e87-8951-406b-84a8-6fecf2841584\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"a9e55e87-8951-406b-84a8-6fecf2841584\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"a9e55e87-8951-406b-84a8-6fecf2841584\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"a9e55e87-8951-406b-84a8-6fecf2841584\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [Prospect match] cached empty result - no API calls, try next matching method {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"a9e55e87-8951-406b-84a8-6fecf2841584\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [Prospect match] Resolved company domain from email {\"email\":\"support@staging.jiminny.com\",\"domain\":\"jiminny.com\"} {\"correlation_id\":\"a9e55e87-8951-406b-84a8-6fecf2841584\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":610497,\"team_id\":2,\"email\":\"veselin.kulov@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"a9e55e87-8951-406b-84a8-6fecf2841584\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":610497,\"participants_processed\":2,\"exact_matches\":0,\"domain_matches\":1,\"best_match_found\":true} {\"correlation_id\":\"a9e55e87-8951-406b-84a8-6fecf2841584\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610497} {\"correlation_id\":\"a9e55e87-8951-406b-84a8-6fecf2841584\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610497,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"a9e55e87-8951-406b-84a8-6fecf2841584\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":610497,\"remote_search\":true,\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89} {\"correlation_id\":\"a9e55e87-8951-406b-84a8-6fecf2841584\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":610490,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89}} {\"correlation_id\":\"209ccee3-3f37-4fb8-8648-ff2f554f6cf3\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610490} {\"correlation_id\":\"209ccee3-3f37-4fb8-8648-ff2f554f6cf3\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610490,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"209ccee3-3f37-4fb8-8648-ff2f554f6cf3\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":610490,\"participants\":[{\"id\":996385,\"user_id\":null,\"contact_id\":null,\"lead_id\":null},{\"id\":996386,\"user_id\":18,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"209ccee3-3f37-4fb8-8648-ff2f554f6cf3\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"209ccee3-3f37-4fb8-8648-ff2f554f6cf3\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"209ccee3-3f37-4fb8-8648-ff2f554f6cf3\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"209ccee3-3f37-4fb8-8648-ff2f554f6cf3\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"209ccee3-3f37-4fb8-8648-ff2f554f6cf3\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"209ccee3-3f37-4fb8-8648-ff2f554f6cf3\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [Prospect match] cached empty result - no API calls, try next matching method {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"209ccee3-3f37-4fb8-8648-ff2f554f6cf3\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [Prospect match] Resolved company domain from email {\"email\":\"support@staging.jiminny.com\",\"domain\":\"jiminny.com\"} {\"correlation_id\":\"209ccee3-3f37-4fb8-8648-ff2f554f6cf3\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":610490,\"team_id\":2,\"email\":\"veselin.kulov@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"209ccee3-3f37-4fb8-8648-ff2f554f6cf3\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":610490,\"participants_processed\":2,\"exact_matches\":0,\"domain_matches\":1,\"best_match_found\":true} {\"correlation_id\":\"209ccee3-3f37-4fb8-8648-ff2f554f6cf3\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610490} {\"correlation_id\":\"209ccee3-3f37-4fb8-8648-ff2f554f6cf3\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610490,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"209ccee3-3f37-4fb8-8648-ff2f554f6cf3\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":610490,\"remote_search\":true,\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89} {\"correlation_id\":\"209ccee3-3f37-4fb8-8648-ff2f554f6cf3\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":610470,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89}} {\"correlation_id\":\"0b6c4d04-c34f-4cfd-b391-aa0128a17792\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610470} {\"correlation_id\":\"0b6c4d04-c34f-4cfd-b391-aa0128a17792\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610470,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"0b6c4d04-c34f-4cfd-b391-aa0128a17792\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":610470,\"participants\":[{\"id\":996369,\"user_id\":null,\"contact_id\":null,\"lead_id\":null},{\"id\":996370,\"user_id\":18,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"0b6c4d04-c34f-4cfd-b391-aa0128a17792\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage for command {\"command\":\"mailbox:text-relay:sync\",\"memoryBeforeCommandInMb\":60.0,\"memoryAfterCommandInMB\":60.0,\"memoryPeakBeforeCommandInMb\":99.727,\"memoryPeakAfterCommandInMB\":99.727} {\"correlation_id\":\"7c0c4db8-2e4e-4aa1-aa71-a3d63cdb81d7\",\"trace_id\":\"b3e9e2f5-3256-429a-ad76-e46e3c2e626d\"}\n[2026-05-11 11:06:28] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"0b6c4d04-c34f-4cfd-b391-aa0128a17792\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"0b6c4d04-c34f-4cfd-b391-aa0128a17792\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"0b6c4d04-c34f-4cfd-b391-aa0128a17792\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"0b6c4d04-c34f-4cfd-b391-aa0128a17792\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"0b6c4d04-c34f-4cfd-b391-aa0128a17792\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [Prospect match] cached empty result - no API calls, try next matching method {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"0b6c4d04-c34f-4cfd-b391-aa0128a17792\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [Prospect match] Resolved company domain from email {\"email\":\"support@staging.jiminny.com\",\"domain\":\"jiminny.com\"} {\"correlation_id\":\"0b6c4d04-c34f-4cfd-b391-aa0128a17792\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":610470,\"team_id\":2,\"email\":\"veselin.kulov@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"0b6c4d04-c34f-4cfd-b391-aa0128a17792\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":610470,\"participants_processed\":2,\"exact_matches\":0,\"domain_matches\":1,\"best_match_found\":true} {\"correlation_id\":\"0b6c4d04-c34f-4cfd-b391-aa0128a17792\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610470} {\"correlation_id\":\"0b6c4d04-c34f-4cfd-b391-aa0128a17792\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610470,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"0b6c4d04-c34f-4cfd-b391-aa0128a17792\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":610470,\"remote_search\":true,\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89} {\"correlation_id\":\"0b6c4d04-c34f-4cfd-b391-aa0128a17792\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":610462,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89}} {\"correlation_id\":\"0584d557-927b-47a5-b193-04786c5c20bc\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610462} {\"correlation_id\":\"0584d557-927b-47a5-b193-04786c5c20bc\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610462,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"0584d557-927b-47a5-b193-04786c5c20bc\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":610462,\"participants\":[{\"id\":996353,\"user_id\":null,\"contact_id\":null,\"lead_id\":null},{\"id\":996354,\"user_id\":18,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"0584d557-927b-47a5-b193-04786c5c20bc\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"0584d557-927b-47a5-b193-04786c5c20bc\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"0584d557-927b-47a5-b193-04786c5c20bc\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"0584d557-927b-47a5-b193-04786c5c20bc\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"0584d557-927b-47a5-b193-04786c5c20bc\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"0584d557-927b-47a5-b193-04786c5c20bc\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [Prospect match] cached empty result - no API calls, try next matching method {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"0584d557-927b-47a5-b193-04786c5c20bc\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [Prospect match] Resolved company domain from email {\"email\":\"support@staging.jiminny.com\",\"domain\":\"jiminny.com\"} {\"correlation_id\":\"0584d557-927b-47a5-b193-04786c5c20bc\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":610462,\"team_id\":2,\"email\":\"veselin.kulov@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"0584d557-927b-47a5-b193-04786c5c20bc\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":610462,\"participants_processed\":2,\"exact_matches\":0,\"domain_matches\":1,\"best_match_found\":true} {\"correlation_id\":\"0584d557-927b-47a5-b193-04786c5c20bc\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610462} {\"correlation_id\":\"0584d557-927b-47a5-b193-04786c5c20bc\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610462,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"0584d557-927b-47a5-b193-04786c5c20bc\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":610462,\"remote_search\":true,\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89} {\"correlation_id\":\"0584d557-927b-47a5-b193-04786c5c20bc\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":610451,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89}} {\"correlation_id\":\"e86be547-45b5-48da-b389-b64c7928f856\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610451} {\"correlation_id\":\"e86be547-45b5-48da-b389-b64c7928f856\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610451,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"e86be547-45b5-48da-b389-b64c7928f856\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":610451,\"participants\":[{\"id\":996340,\"user_id\":null,\"contact_id\":null,\"lead_id\":null},{\"id\":996341,\"user_id\":18,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"e86be547-45b5-48da-b389-b64c7928f856\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"e86be547-45b5-48da-b389-b64c7928f856\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"e86be547-45b5-48da-b389-b64c7928f856\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"e86be547-45b5-48da-b389-b64c7928f856\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"e86be547-45b5-48da-b389-b64c7928f856\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"e86be547-45b5-48da-b389-b64c7928f856\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [Prospect match] cached empty result - no API calls, try next matching method {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"e86be547-45b5-48da-b389-b64c7928f856\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [Prospect match] Resolved company domain from email {\"email\":\"support@staging.jiminny.com\",\"domain\":\"jiminny.com\"} {\"correlation_id\":\"e86be547-45b5-48da-b389-b64c7928f856\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":610451,\"team_id\":2,\"email\":\"veselin.kulov@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"e86be547-45b5-48da-b389-b64c7928f856\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":610451,\"participants_processed\":2,\"exact_matches\":0,\"domain_matches\":1,\"best_match_found\":true} {\"correlation_id\":\"e86be547-45b5-48da-b389-b64c7928f856\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610451} {\"correlation_id\":\"e86be547-45b5-48da-b389-b64c7928f856\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610451,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"e86be547-45b5-48da-b389-b64c7928f856\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":610451,\"remote_search\":true,\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89} {\"correlation_id\":\"e86be547-45b5-48da-b389-b64c7928f856\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":610438,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89}} {\"correlation_id\":\"34e69f21-acca-487a-8565-fc42df81c0fe\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610438} {\"correlation_id\":\"34e69f21-acca-487a-8565-fc42df81c0fe\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610438,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"34e69f21-acca-487a-8565-fc42df81c0fe\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":610438,\"participants\":[{\"id\":996320,\"user_id\":null,\"contact_id\":null,\"lead_id\":null},{\"id\":996321,\"user_id\":18,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"34e69f21-acca-487a-8565-fc42df81c0fe\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"34e69f21-acca-487a-8565-fc42df81c0fe\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"34e69f21-acca-487a-8565-fc42df81c0fe\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"34e69f21-acca-487a-8565-fc42df81c0fe\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"34e69f21-acca-487a-8565-fc42df81c0fe\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"34e69f21-acca-487a-8565-fc42df81c0fe\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [Prospect match] cached empty result - no API calls, try next matching method {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"34e69f21-acca-487a-8565-fc42df81c0fe\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [Prospect match] Resolved company domain from email {\"email\":\"support@staging.jiminny.com\",\"domain\":\"jiminny.com\"} {\"correlation_id\":\"34e69f21-acca-487a-8565-fc42df81c0fe\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":610438,\"team_id\":2,\"email\":\"veselin.kulov@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"34e69f21-acca-487a-8565-fc42df81c0fe\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":610438,\"participants_processed\":2,\"exact_matches\":0,\"domain_matches\":1,\"best_match_found\":true} {\"correlation_id\":\"34e69f21-acca-487a-8565-fc42df81c0fe\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610438} {\"correlation_id\":\"34e69f21-acca-487a-8565-fc42df81c0fe\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610438,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"34e69f21-acca-487a-8565-fc42df81c0fe\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":610438,\"remote_search\":true,\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89} {\"correlation_id\":\"34e69f21-acca-487a-8565-fc42df81c0fe\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":610426,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89}} {\"correlation_id\":\"813c1b3a-95b1-4314-b468-5f42e908f99e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610426} {\"correlation_id\":\"813c1b3a-95b1-4314-b468-5f42e908f99e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610426,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"813c1b3a-95b1-4314-b468-5f42e908f99e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":610426,\"participants\":[{\"id\":996306,\"user_id\":null,\"contact_id\":null,\"lead_id\":null},{\"id\":996307,\"user_id\":18,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"813c1b3a-95b1-4314-b468-5f42e908f99e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"813c1b3a-95b1-4314-b468-5f42e908f99e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"813c1b3a-95b1-4314-b468-5f42e908f99e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"813c1b3a-95b1-4314-b468-5f42e908f99e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"813c1b3a-95b1-4314-b468-5f42e908f99e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"813c1b3a-95b1-4314-b468-5f42e908f99e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [Prospect match] cached empty result - no API calls, try next matching method {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"813c1b3a-95b1-4314-b468-5f42e908f99e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [Prospect match] Resolved company domain from email {\"email\":\"support@staging.jiminny.com\",\"domain\":\"jiminny.com\"} {\"correlation_id\":\"813c1b3a-95b1-4314-b468-5f42e908f99e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":610426,\"team_id\":2,\"email\":\"veselin.kulov@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"813c1b3a-95b1-4314-b468-5f42e908f99e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":610426,\"participants_processed\":2,\"exact_matches\":0,\"domain_matches\":1,\"best_match_found\":true} {\"correlation_id\":\"813c1b3a-95b1-4314-b468-5f42e908f99e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610426} {\"correlation_id\":\"813c1b3a-95b1-4314-b468-5f42e908f99e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610426,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"813c1b3a-95b1-4314-b468-5f42e908f99e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":610426,\"remote_search\":true,\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89} {\"correlation_id\":\"813c1b3a-95b1-4314-b468-5f42e908f99e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":610403,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89}} {\"correlation_id\":\"98026431-b755-4e7e-af17-86d8e2993031\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610403} {\"correlation_id\":\"98026431-b755-4e7e-af17-86d8e2993031\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610403,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"98026431-b755-4e7e-af17-86d8e2993031\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":610403,\"participants\":[{\"id\":996282,\"user_id\":null,\"contact_id\":null,\"lead_id\":null},{\"id\":996283,\"user_id\":18,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"98026431-b755-4e7e-af17-86d8e2993031\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"98026431-b755-4e7e-af17-86d8e2993031\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"98026431-b755-4e7e-af17-86d8e2993031\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"98026431-b755-4e7e-af17-86d8e2993031\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"98026431-b755-4e7e-af17-86d8e2993031\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"98026431-b755-4e7e-af17-86d8e2993031\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [Prospect match] cached empty result - no API calls, try next matching method {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"98026431-b755-4e7e-af17-86d8e2993031\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [Prospect match] Resolved company domain from email {\"email\":\"support@staging.jiminny.com\",\"domain\":\"jiminny.com\"} {\"correlation_id\":\"98026431-b755-4e7e-af17-86d8e2993031\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":610403,\"team_id\":2,\"email\":\"veselin.kulov@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"98026431-b755-4e7e-af17-86d8e2993031\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":610403,\"participants_processed\":2,\"exact_matches\":0,\"domain_matches\":1,\"best_match_found\":true} {\"correlation_id\":\"98026431-b755-4e7e-af17-86d8e2993031\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610403} {\"correlation_id\":\"98026431-b755-4e7e-af17-86d8e2993031\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610403,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"98026431-b755-4e7e-af17-86d8e2993031\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":610403,\"remote_search\":true,\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89} {\"correlation_id\":\"98026431-b755-4e7e-af17-86d8e2993031\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":610400,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":4487,\"account_id\":244,\"opportunity_id\":350,\"stage_id\":34}} {\"correlation_id\":\"4c991921-b335-438b-a656-aac035483aa5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610400} {\"correlation_id\":\"4c991921-b335-438b-a656-aac035483aa5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610400,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"4c991921-b335-438b-a656-aac035483aa5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":610400,\"participants\":[{\"id\":996275,\"user_id\":1460,\"contact_id\":null,\"lead_id\":null},{\"id\":996276,\"user_id\":206,\"contact_id\":null,\"lead_id\":null},{\"id\":996277,\"user_id\":null,\"contact_id\":4487,\"lead_id\":null}]} {\"correlation_id\":\"4c991921-b335-438b-a656-aac035483aa5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"4c991921-b335-438b-a656-aac035483aa5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"4c991921-b335-438b-a656-aac035483aa5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"4c991921-b335-438b-a656-aac035483aa5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"4c991921-b335-438b-a656-aac035483aa5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":610400,\"team_id\":2,\"email\":\"aneliya.angelova@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"4c991921-b335-438b-a656-aac035483aa5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":610400,\"team_id\":2,\"email\":\"horen.kirazyan@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"4c991921-b335-438b-a656-aac035483aa5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: ProspectCache - Searching DB for opportunity by owner {\"account_id\":244,\"contact_id\":4487,\"owner_id\":1460} {\"correlation_id\":\"4c991921-b335-438b-a656-aac035483aa5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: ProspectCache - Fallback DB opportunity search {\"account_id\":244,\"contact_id\":4487} {\"correlation_id\":\"4c991921-b335-438b-a656-aac035483aa5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: ProspectCache - Opportunity DB search results {\"account_id\":244,\"contact_id\":4487,\"opportunity_id\":350} {\"correlation_id\":\"4c991921-b335-438b-a656-aac035483aa5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"robinson@crusoe.com\"} {\"correlation_id\":\"4c991921-b335-438b-a656-aac035483aa5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":610400,\"participants_processed\":3,\"exact_matches\":1,\"domain_matches\":0,\"best_match_found\":true} {\"correlation_id\":\"4c991921-b335-438b-a656-aac035483aa5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610400} {\"correlation_id\":\"4c991921-b335-438b-a656-aac035483aa5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610400,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"4c991921-b335-438b-a656-aac035483aa5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":610400,\"remote_search\":true,\"lead_id\":null,\"contact_id\":4487,\"account_id\":244,\"opportunity_id\":350,\"stage_id\":34} {\"correlation_id\":\"4c991921-b335-438b-a656-aac035483aa5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [ EsUpdateProcessManager ] Finished updating entities in ES {\"worker\":\"\",\"peak_memory\":\"99.73 MB\",\"elapsed_seconds\":0.14,\"update_target\":\"activities\",\"should_iterate_again\":false} {\"correlation_id\":\"38935ae1-fcb3-437e-9c9e-1b7415a0b7e1\",\"trace_id\":\"325a2f9b-9c66-4223-ac02-1a43723f4315\"}\n[2026-05-11 11:06:30] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"conference:pre-meeting-notification\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.727} {\"correlation_id\":\"4fa18424-38b1-4f8a-a2e1-b467db754dba\",\"trace_id\":\"0d2c5c78-f780-4c85-81ff-c26ad669374b\"}\n[2026-05-11 11:06:30] local.INFO: Running pre-meeting notification command {\"correlation_id\":\"4fa18424-38b1-4f8a-a2e1-b467db754dba\",\"trace_id\":\"0d2c5c78-f780-4c85-81ff-c26ad669374b\"}\n[2026-05-11 11:06:30] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage for command {\"command\":\"conference:pre-meeting-notification\",\"memoryBeforeCommandInMb\":60.0,\"memoryAfterCommandInMB\":60.0,\"memoryPeakBeforeCommandInMb\":99.727,\"memoryPeakAfterCommandInMB\":99.727} {\"correlation_id\":\"4fa18424-38b1-4f8a-a2e1-b467db754dba\",\"trace_id\":\"0d2c5c78-f780-4c85-81ff-c26ad669374b\"}\n[2026-05-11 11:06:31] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"conference:monitor:start\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.727} {\"correlation_id\":\"ad10418d-4623-4ff3-b748-2e19330019b3\",\"trace_id\":\"a31c4682-7665-42af-99bd-8ab71b6cd9ac\"}\n[2026-05-11 11:06:31] local.INFO: Running conference:monitor:start command for activities in (2026-05-11 10:56:00, 2026-05-11 11:01:00] {\"correlation_id\":\"ad10418d-4623-4ff3-b748-2e19330019b3\",\"trace_id\":\"a31c4682-7665-42af-99bd-8ab71b6cd9ac\"}\n[2026-05-11 11:06:31] local.INFO: [conference:monitor:start] start ok {\"activity_id\":484852} {\"correlation_id\":\"ad10418d-4623-4ff3-b748-2e19330019b3\",\"trace_id\":\"a31c4682-7665-42af-99bd-8ab71b6cd9ac\"}\n[2026-05-11 11:06:31] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage for command {\"command\":\"conference:monitor:start\",\"memoryBeforeCommandInMb\":60.0,\"memoryAfterCommandInMB\":60.0,\"memoryPeakBeforeCommandInMb\":99.727,\"memoryPeakAfterCommandInMB\":99.727} {\"correlation_id\":\"ad10418d-4623-4ff3-b748-2e19330019b3\",\"trace_id\":\"a31c4682-7665-42af-99bd-8ab71b6cd9ac\"}\n[2026-05-11 11:06:33] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"conference:monitor:end\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.727} {\"correlation_id\":\"31a09cc3-a94d-4145-ab23-b041b077106a\",\"trace_id\":\"82ffd281-4725-402c-90c7-162549d4415a\"}\n[2026-05-11 11:06:33] local.INFO: conference:monitor:end:Jiminny\\Console\\Commands\\Activities\\MonitorMeetingEndCommand::logActivitiesEnded {\"from\":\"11:01\",\"to\":\"11:06\"} {\"correlation_id\":\"31a09cc3-a94d-4145-ab23-b041b077106a\",\"trace_id\":\"82ffd281-4725-402c-90c7-162549d4415a\"}\n[2026-05-11 11:06:33] local.INFO: conference:monitor:end:Jiminny\\Console\\Commands\\Activities\\MonitorMeetingEndCommand::logActivitiesWithUnfinishedSession {\"from\":\"00:56\",\"to\":\"01:01\"} {\"correlation_id\":\"31a09cc3-a94d-4145-ab23-b041b077106a\",\"trace_id\":\"82ffd281-4725-402c-90c7-162549d4415a\"}\n[2026-05-11 11:06:33] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage for command {\"command\":\"conference:monitor:end\",\"memoryBeforeCommandInMb\":60.0,\"memoryAfterCommandInMB\":60.0,\"memoryPeakBeforeCommandInMb\":99.727,\"memoryPeakAfterCommandInMB\":99.727} {\"correlation_id\":\"31a09cc3-a94d-4145-ab23-b041b077106a\",\"trace_id\":\"82ffd281-4725-402c-90c7-162549d4415a\"}\n[2026-05-11 11:06:35] local.NOTICE: Repairing HubSpot tokens start {\"correlation_id\":\"b78a7ca3-5d72-4b23-8794-4543dd6d8ffc\",\"trace_id\":\"bedc11e2-b932-44b3-a16d-436f13a1254e\"}\n[2026-05-11 11:06:35] local.INFO: Trying to refresh HubSpot token {\"account_id\":59,\"updated_at\":\"2025-10-03 09:32:05\"} {\"correlation_id\":\"b78a7ca3-5d72-4b23-8794-4543dd6d8ffc\",\"trace_id\":\"bedc11e2-b932-44b3-a16d-436f13a1254e\"}\n[2026-05-11 11:06:35] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"b78a7ca3-5d72-4b23-8794-4543dd6d8ffc\",\"trace_id\":\"bedc11e2-b932-44b3-a16d-436f13a1254e\"}\n[2026-05-11 11:06:35] local.INFO: [SocialAccountService] Refreshing token from provider {\"socialAccountId\":59,\"provider\":\"hubspot\",\"refreshToken\":\"97b78f6e2cc49965c00c2492b602b02708b1392551e6b3f113fbaa48992af90b\",\"state\":\"full-refresh\"} {\"correlation_id\":\"b78a7ca3-5d72-4b23-8794-4543dd6d8ffc\",\"trace_id\":\"bedc11e2-b932-44b3-a16d-436f13a1254e\"}\n[2026-05-11 11:06:35] local.ERROR: Failed to refresh HubSpot token {\"account_id\":59,\"updated_at\":\"2025-10-03 09:32:05\",\"reason\":\"missing or invalid refresh token\",\"previous\":\"\"} {\"correlation_id\":\"b78a7ca3-5d72-4b23-8794-4543dd6d8ffc\",\"trace_id\":\"bedc11e2-b932-44b3-a16d-436f13a1254e\"}\n[2026-05-11 11:06:35] local.INFO: Trying to refresh HubSpot token {\"account_id\":306,\"updated_at\":\"2023-11-27 09:30:03\"} {\"correlation_id\":\"b78a7ca3-5d72-4b23-8794-4543dd6d8ffc\",\"trace_id\":\"bedc11e2-b932-44b3-a16d-436f13a1254e\"}\n[2026-05-11 11:06:35] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"b78a7ca3-5d72-4b23-8794-4543dd6d8ffc\",\"trace_id\":\"bedc11e2-b932-44b3-a16d-436f13a1254e\"}\n[2026-05-11 11:06:35] local.INFO: [SocialAccountService] Refreshing token from provider {\"socialAccountId\":306,\"provider\":\"hubspot\",\"refreshToken\":\"6fa6aa8cc641d131231acc3470f5c03cb3b07b2e580fb18f8acb3b1dbb72549b\",\"state\":\"full-refresh\"} {\"correlation_id\":\"b78a7ca3-5d72-4b23-8794-4543dd6d8ffc\",\"trace_id\":\"bedc11e2-b932-44b3-a16d-436f13a1254e\"}\n[2026-05-11 11:06:36] local.ERROR: Failed to refresh HubSpot token {\"account_id\":306,\"updated_at\":\"2023-11-27 09:30:03\",\"reason\":\"missing or invalid refresh token\",\"previous\":\"\"} {\"correlation_id\":\"b78a7ca3-5d72-4b23-8794-4543dd6d8ffc\",\"trace_id\":\"bedc11e2-b932-44b3-a16d-436f13a1254e\"}\n[2026-05-11 11:06:36] local.INFO: Trying to refresh HubSpot token {\"account_id\":1372,\"updated_at\":\"2025-10-02 14:47:06\"} {\"correlation_id\":\"b78a7ca3-5d72-4b23-8794-4543dd6d8ffc\",\"trace_id\":\"bedc11e2-b932-44b3-a16d-436f13a1254e\"}\n[2026-05-11 11:06:36] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"b78a7ca3-5d72-4b23-8794-4543dd6d8ffc\",\"trace_id\":\"bedc11e2-b932-44b3-a16d-436f13a1254e\"}\n[2026-05-11 11:06:36] local.INFO: [SocialAccountService] Refreshing token from provider {\"socialAccountId\":1372,\"provider\":\"hubspot\",\"refreshToken\":\"9aa73948c761da29dce46c177cf9aee1fde483a44169ca38723f9f0597d7a8c4\",\"state\":\"full-refresh\"} {\"correlation_id\":\"b78a7ca3-5d72-4b23-8794-4543dd6d8ffc\",\"trace_id\":\"bedc11e2-b932-44b3-a16d-436f13a1254e\"}\n[2026-05-11 11:06:36] local.ERROR: Failed to refresh HubSpot token {\"account_id\":1372,\"updated_at\":\"2025-10-02 14:47:06\",\"reason\":\"missing or invalid refresh token\",\"previous\":\"\"} {\"correlation_id\":\"b78a7ca3-5d72-4b23-8794-4543dd6d8ffc\",\"trace_id\":\"bedc11e2-b932-44b3-a16d-436f13a1254e\"}\n[2026-05-11 11:06:36] local.NOTICE: Repairing HubSpot tokens end {\"total\":3,\"fixed\":0,\"failed\":3} {\"correlation_id\":\"b78a7ca3-5d72-4b23-8794-4543dd6d8ffc\",\"trace_id\":\"bedc11e2-b932-44b3-a16d-436f13a1254e\"}\n[2026-05-11 11:06:40] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"crm:bullhorn:ping\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.727} {\"correlation_id\":\"7f93fd49-8b24-4f96-a532-a4777ccf3183\",\"trace_id\":\"14797017-4a40-4a20-a018-dfa389b58256\"}\n[2026-05-11 11:06:40] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"conference:pre-meeting-reminder\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.727} {\"correlation_id\":\"00c329ca-e23d-4d27-974c-c9cce4940adf\",\"trace_id\":\"d29b535e-a485-4bb8-bcf4-61cc79b73da5\"}\n[2026-05-11 11:06:40] local.INFO: [HubSpot Journal Command] Starting polling service {\"correlation_id\":\"32a337c8-7047-40f4-b1f4-d3008f51e4da\",\"trace_id\":\"b65ab282-ade3-48d8-afd0-798307edc610\"}\n[2026-05-11 11:06:40] local.INFO: [HubSpot Journal Polling] Service starting {\"memory_limit\":\"256M\",\"max_execution_time\":\"0\",\"initial_memory_mb\":60.0} {\"correlation_id\":\"32a337c8-7047-40f4-b1f4-d3008f51e4da\",\"trace_id\":\"b65ab282-ade3-48d8-afd0-798307edc610\"}\n[2026-05-11 11:06:40] local.INFO: [HubSpot Journal Polling] Acquired polling lock {\"expires_at\":\"2026-05-11T11:08:40.813273Z\"} {\"correlation_id\":\"32a337c8-7047-40f4-b1f4-d3008f51e4da\",\"trace_id\":\"b65ab282-ade3-48d8-afd0-798307edc610\"}\n[2026-05-11 11:06:40] local.INFO: [HubSpot Journal Auth] Requesting new client credentials token {\"correlation_id\":\"32a337c8-7047-40f4-b1f4-d3008f51e4da\",\"trace_id\":\"b65ab282-ade3-48d8-afd0-798307edc610\"}\n[2026-05-11 11:06:40] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage for command {\"command\":\"crm:bullhorn:ping\",\"memoryBeforeCommandInMb\":60.0,\"memoryAfterCommandInMB\":60.0,\"memoryPeakBeforeCommandInMb\":99.727,\"memoryPeakAfterCommandInMB\":99.727} {\"correlation_id\":\"7f93fd49-8b24-4f96-a532-a4777ccf3183\",\"trace_id\":\"14797017-4a40-4a20-a018-dfa389b58256\"}\n[2026-05-11 11:06:40] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage for command {\"command\":\"conference:pre-meeting-reminder\",\"memoryBeforeCommandInMb\":60.0,\"memoryAfterCommandInMB\":60.0,\"memoryPeakBeforeCommandInMb\":99.727,\"memoryPeakAfterCommandInMB\":99.727} {\"correlation_id\":\"00c329ca-e23d-4d27-974c-c9cce4940adf\",\"trace_id\":\"d29b535e-a485-4bb8-bcf4-61cc79b73da5\"}\n[2026-05-11 11:06:41] local.INFO: [HubSpot Journal Auth] Successfully obtained new access token {\"expires_in\":1800,\"cached_for\":1500} {\"correlation_id\":\"32a337c8-7047-40f4-b1f4-d3008f51e4da\",\"trace_id\":\"b65ab282-ade3-48d8-afd0-798307edc610\"}\n[2026-05-11 11:06:41] local.INFO: [HubSpot Journal Polling] No data {\"correlation_id\":\"32a337c8-7047-40f4-b1f4-d3008f51e4da\",\"trace_id\":\"b65ab282-ade3-48d8-afd0-798307edc610\"}\n[2026-05-11 11:06:46] local.INFO: [HubSpot Journal Polling] No data {\"correlation_id\":\"32a337c8-7047-40f4-b1f4-d3008f51e4da\",\"trace_id\":\"b65ab282-ade3-48d8-afd0-798307edc610\"}","role_description":"text entry area","is_enabled":true,"is_focused":true,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"on_screen":false,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"bounds":{"left":0.011968086,"top":0.047885075,"width":0.024268618,"height":0.024740623},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
-5998074271716991012
|
4180792001263167613
|
visual_change
|
accessibility
|
NULL
|
Project: faVsco.js, menu
JY-20725-handle-HS-search Project: faVsco.js, menu
JY-20725-handle-HS-search-rate-limit, menu
Start Listening for PHP Debug Connections
HandleHubspotRateLimitTest
Run 'HandleHubspotRateLimitTest'
Debug 'HandleHubspotRateLimitTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
5
133
11
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Console\Commands;
use Carbon\Carbon;
use Carbon\CarbonImmutable;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Redis;
use InvalidArgumentException;
use Jiminny\Jobs\AutomatedReports\RequestGenerateAskJiminnyReportJob;
use Jiminny\Jobs\AutomatedReports\SendReportMailJob;
use Jiminny\Jobs\Crm\Delete\VerifyActivityCrmTaskJob;
use Jiminny\Jobs\Crm\MatchActivityCrmData;
use Jiminny\Jobs\JobDispatcherInterface;
use Jiminny\Models\Activity;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\AutomatedReportResult;
use Jiminny\Models\Team;
use Jiminny\Models\User;
use Jiminny\Repositories\AutomatedReportsRepository;
use Jiminny\Services\Activity\CrmOwnerResolver;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
/**
* Class JiminnyDebugCommand
*
* @package Jiminny\Console\Commands
*/
class JiminnyDebugCommand extends Command
{
public const string FREQUENCY_DAILY = 'daily';
public const string FREQUENCY_WEEKLY = 'weekly';
public const string FREQUENCY_MONTHLY = 'monthly';
public const string FREQUENCY_QUARTERLY = 'quarterly';
public const string FREQUENCY_ONE_OFF = 'one_off';
protected $signature = 'jiminny:debug';
public function handle(
JobDispatcherInterface $jobDispatcher,
AutomatedReportsService $automatedReportsService,
AutomatedReportsRepository $automatedReportsRepository,
UserPilotClient $userPilotClient
): void {
// Choose ONE of the following to run, then comment out the others.
// 1) Dispatch a storm of MatchActivityCrmData jobs against team 2
$this->simulateMatchActivityStorm(teamId: 2, count: 100);
// 2) Dispatch a storm of VerifyActivityCrmTaskJob jobs (simulates DeleteCrmEntityTrait fan-out)
// $this->simulateVerifyTaskStorm(teamId: 2, count: 100);
// 3) Inspect Redis circuit-breaker state for the team's HubSpot portal
// $this->observeRateLimitCache(teamId: 2);
// 4) Make 3 synchronous matchByName calls (foreground, hits API directly)
// $this->rateLimit();
exit(1);
$report = AutomatedReport::find(71);
$last = AutomatedReportResult::query()
->where('report_id', $report->getId())
->whereIn('status', [AutomatedReportResult::STATUS_DEFAULT, AutomatedReportResult::STATUS_FAILED])
// ->where('reason', '!=', AutomatedReportResult::REASON_NOT_ENOUGH_ACTIVITIES)
->whereDate('created_at', CarbonImmutable::now()->toDateString())
->latest()
->first();
$this->info("Last: {$last->getId()}");
exit(1);
$user = User::find(143);
// $count = $automatedReportsRepository->countUserReports($user);
// $this->info("Count: {$count}");
// $count = $automatedReportsRepository->countAllUserReports($user);
// $this->info("All count: {$count}");
$payload = [
'report_type' => 'ask_jiminny',
'frequency' => 'weekly',
];
$userPilotClient->track($user, 'ask-jiminny-report-generated', $payload);
exit(1);
$now = Carbon::now()->subDay(1);
$this->info("Now: {$now->toDateTimeString()}");
$weekStart = Carbon::getWeekStartsAt();
$this->info("Now: {$weekStart}");
// $from = $now->copy()->previousWeekday()->startOfDay();
// $to = $now->copy()->previousWeekday()->endOfDay();
// $fromOld = $now->copy()->subWeeks(1)->startOfDay();
// $toOld = $now->copy()->subDay()->endOfDay();
// $fromNew = $now->copy()->subWeek()->startOfWeek();
// $toNew = $now->copy()->subWeek()->endOfWeek();
// $fromOld = $now->copy()->subMonths(1)->startOfDay();
// $toOld = $now->copy()->subDay()->endOfDay();
// $fromNew = $now->copy()->subMonthNoOverflow()->startOfMonth();
// $toNew = $now->copy()->subMonthNoOverflow()->endOfMonth();
$fromOld = $now->copy()->subMonths(3)->startOfDay();
$toOld = $now->copy()->subDay()->endOfDay();
$fromNew = $now->copy()->subQuarterNoOverflow()->startOfQuarter();
$toNew = $now->copy()->subQuarterNoOverflow()->endOfQuarter();
$this->info("From old: {$fromOld->toDateTimeString()}");
$this->info("To old: {$toOld->toDateTimeString()}");
$this->info("From new: {$fromNew->toDateTimeString()}");
$this->info("To new: {$toNew->toDateTimeString()}");
exit(1);
$report = AutomatedReport::find(71);
$job = new RequestGenerateAskJiminnyReportJob($report->getUuid());
$jobDispatcher->dispatch($job);
exit(1);
// $this->formatDate($jobDispatcher);
// $this->sendMail($jobDispatcher, $automatedReportsService);
// $this->crmService();
$this->getPayload($automatedReportsService);
exit(1);
}
private function crmService()
{
$activity = Activity::find(418141);
$team = Team::find(19);
$config = $team->getCrmConfiguration();
$crmResolver = app(CrmOwnerResolver::class, [
'team' => $team,
'integrationAdmin' => $team->getOwner(),
'providerSlug' => $config->getProviderName(),
]);
$crmService = $crmResolver->prepareCrmService();
$crmService->createTranscriptNotes($activity);
}
private function sendMail(JobDispatcherInterface $jobDispatcher, AutomatedReportsService $automatedReportsService)
{
$reportUuid = '';
// $report = $automatedReportsService->getReportResult($reportUuid);
$report = AutomatedReportResult::find(275);
$validRecipients = $automatedReportsService->getValidRecipientUsers(
$report->getReport(),
includeJiminny: true,
);
$recipient = $validRecipients[0];
$fileName = $automatedReportsService->getReportFileName($report);
$typeName = $report->getReport()->getCustomName()
?? $automatedReportsService->getReportTypeName($report);
$teamsName = $automatedReportsService->getReportTeamsName($report);
$periodName = $automatedReportsService->getReportPeriodName($report);
$s3Path = $automatedReportsService->getMediaPath($report);
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$fileName ' . PHP_EOL . print_r($fileName, true));
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$typeName ' . PHP_EOL . print_r($typeName, true));
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$teamsName ' . PHP_EOL . print_r($teamsName, true));
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$periodName ' . PHP_EOL . print_r($periodName, true));
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$s3Path ' . PHP_EOL . print_r($s3Path, true));
$jobDispatcher->dispatch(
new SendReportMailJob(
reportUuid: $report->getUuid(),
s3Path: $s3Path,
recipientEmail: $recipient['email'],
recipientName: $recipient['name'] ?? null,
fileName: $fileName,
typeName: $typeName,
teamsName: $teamsName,
periodName: $periodName,
isAskJiminny: true,
)
);
exit(1);
}
private function formatDate(JobDispatcherInterface $jobDispatcher): void
{
$customName = 'Custom report name';
// $frequency = self::FREQUENCY_DAILY;
// $frequency = self::FREQUENCY_WEEKLY;
$frequency = self::FREQUENCY_MONTHLY;
// $frequency = self::FREQUENCY_QUARTERLY;
// $frequency = self::FREQUENCY_ONE_OFF;
$period = $this->calculateFromAndToDatePeriod($frequency);
$from = $period['fromDate'];
$to = $period['toDate'];
$periodName = $this->formatReportPeriodName($frequency, $from, $to);
$filenameSuffix = null;
if ($customName) {
if ($filenameSuffix) {
$customName .= " {$filenameSuffix}";
}
$result = $this->sanitizeFileName("{$customName} - {$periodName}");
}
$this->info($result);
}
public function calculateFromAndToDatePeriod(
string $frequency,
?Carbon $fromDate = null,
?Carbon $toDate = null
): array {
if ($frequency === self::FREQUENCY_ONE_OFF) {
return [
'fromDate' => $fromDate,
'toDate' => $toDate,
];
}
$now = Carbon::now();
return match ($frequency) {
self::FREQUENCY_DAILY => [
'fromDate' => $now->copy()->subDay()->startOfDay(),
'toDate' => $now->copy()->subDay()->endOfDay(),
],
self::FREQUENCY_WEEKLY => [
'fromDate' => $now->copy()->subWeeks(1)->startOfDay(),
'toDate' => $now->copy()->subDay()->endOfDay(),
],
self::FREQUENCY_MONTHLY => [
'fromDate' => $now->copy()->subMonths(1)->startOfDay(),
'toDate' => $now->copy()->subDay()->endOfDay(),
],
self::FREQUENCY_QUARTERLY => [
'fromDate' => $now->copy()->subMonths(3)->startOfDay(),
'toDate' => $now->copy()->subDay()->endOfDay(),
],
default => throw new InvalidArgumentException("Unsupported frequency: {$frequency}"),
};
}
private function formatReportPeriodName(string $frequency, Carbon $from, Carbon $to): string
{
$fromYear = $from->format('Y');
$toYear = $to->format('Y');
$differentYears = $fromYear !== $toYear;
switch ($frequency) {
case self::FREQUENCY_DAILY:
return $from->format('j M Y');
case self::FREQUENCY_QUARTERLY:
// 'Jan-Mar 2025' or 'Nov 2024-Jan 2025' if years differ
$startMonth = $from->format('M');
$endMonth = $to->copy()->subMonth();
$endMonthName = $endMonth->format('M');
$endMonthYear = $endMonth->format('Y');
if ($differentYears) {
return "{$startMonth} {$fromYear} - {$endMonthName} {$endMonthYear}";
}
return "{$startMonth} - {$endMonthName} {$toYear}";
case self::FREQUENCY_MONTHLY:
// 'May 2025' - monthly reports are always within the same year
return $from->format('M Y');
case self::FREQUENCY_WEEKLY:
// '4 - 8 Aug 2025', '27 Oct - 3 Nov 2025', or '28 Dec 2024 - 3 Jan 2025' if years differ
$startDay = $from->format('j');
$endDay = $to->format('j');
$startMonth = $from->format('M');
$endMonth = $to->format('M');
if ($differentYears) {
return "{$startDay} {$startMonth} {$fromYear} - {$endDay} {$endMonth} {$toYear}";
}
if ($startMonth !== $endMonth) {
return "{$startDay} {$startMonth} - {$endDay} {$endMonth} {$toYear}";
}
return "{$startDay} - {$endDay} {$endMonth} {$toYear}";
case self::FREQUENCY_ONE_OFF:
// '2 May-31 May 2025' or '15 Dec 2024-15 Jan 2025' if years differ
$startDay = $from->format('j');
$startMonth = $from->format('M');
$endDay = $to->format('j');
$endMonth = $to->format('M');
// If same month and year, use a format like '2-31 May 2025'
if ($startMonth === $endMonth && ! $differentYears) {
return "{$startDay} - {$endDay} {$startMonth} {$toYear}";
}
// If different years, include both years
if ($differentYears) {
return "{$startDay} {$startMonth} {$fromYear} - {$endDay} {$endMonth} {$toYear}";
}
// Same year but different months
return "{$startDay} {$startMonth} - {$endDay} {$endMonth} {$toYear}";
default:
// Default format for unknown frequencies
return $from->format('j M Y') . ' - ' . $to->format('j M Y');
}
}
public function sanitizeFileName(string $fileName): string
{
return str_replace(['/', '\\'], '-', $fileName);
}
private function getPayload(AutomatedReportsService $automatedReportsService)
{
$reportResult = AutomatedReportResult::find(269);
$automatedReport = $reportResult->getReport();
$activityIds = [1,2,3];
$payload = $automatedReportsService->getAskJiminnyGenerateReportPayload(
automatedReport: $automatedReport,
reportResult: $reportResult,
activityIds: $activityIds,
);
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$payload ' . PHP_EOL . print_r($payload, true));
}
private function rateLimit()
{
$team = Team::find(2);
$config = $team->getCrmConfiguration();
$crmResolver = app(CrmOwnerResolver::class, [
'team' => $team,
'integrationAdmin' => $team->getOwner(),
'providerSlug' => $config->getProviderName(),
]);
$crmService = $crmResolver->prepareCrmService();
for ($i = 0 ; $i < 3; $i++) {
// if ($i % 25 === 0) {
// $this->info("Syncing opportunity {$i}");
$this->info("Matching contact {$i}");
// }
// $crmService->syncOpportunity('374720564');
$crmService->matchByName('Robot');
}
}
private function simulateMatchActivityStorm(int $teamId = 2, int $count = 100): void
{
$team = Team::find($teamId);
$config = $team->getCrmConfiguration();
$activities = Activity::query()
->where('crm_configuration_id', $config->getId())
->orderByDesc('id')
->limit($count)
->get();
$this->info("Dispatching {$activities->count()} MatchActivityCrmData jobs (portal={$config->getId()})");
foreach ($activities as $activity) {
MatchActivityCrmData::dispatch($activity->getId(), $config, true);
}
$this->info('Done. Watch logs and run jiminny:debug observeRateLimit to inspect cache state.');
}
private function simulateVerifyTaskStorm(int $teamId = 2, int $count = 100): void
{
$activities = Activity::query()
->where('team_id', $teamId)
->whereNotNull('crm_provider_id')
->orderByDesc('id')
->limit($count)
->get();
$this->info("Dispatching {$activities->count()} VerifyActivityCrmTaskJob jobs");
foreach ($activities as $activity) {
VerifyActivityCrmTaskJob::dispatch($activity->getId());
}
$this->info('Done.');
}
private function observeRateLimitCache(int $teamId = 2): void
{
$team = Team::find($teamId);
$config = $team->getCrmConfiguration();
$key = sprintf('hubspot:ratelimit:portal:%d', $config->getId());
$value = Redis::get($key);
$ttl = Redis::ttl($key);
$this->info("Redis key: {$key}");
$this->info('Value: ' . ($value ?? '(empty)'));
$this->info("TTL: {$ttl}s");
}
}
Show Replace Field
Search History
429
New Line
Match Case
Words
Regex
Replace History
Replace
New Line
Preserve case
1/2
Previous Occurrence
Next Occurrence
Filter Search Results
Open in Window, Multiple Cursors
Click to highlight
Close
Sync Changes
Hide This Notification
Code changed:
Hide
174
Previous Highlighted Error
Next Highlighted Error
[2026-05-11 11:06:08] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {"activity_id":612562,"team_id":2,"email":"[EMAIL]"} {"correlation_id":"c1949c5e-c0f4-431e-ba26-59d49408d871","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:08] local.INFO: [CrmActivityService] CRM matching completed {"activity_id":612562,"participants_processed":2,"exact_matches":0,"domain_matches":1,"best_match_found":true} {"correlation_id":"c1949c5e-c0f4-431e-ba26-59d49408d871","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:08] local.INFO: [ EsUpdateTarget ] Update single target {"target":"activities","purpose":"searchable-observer-update","entityId":612562} {"correlation_id":"c1949c5e-c0f4-431e-ba26-59d49408d871","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:08] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {"entityType":"activities","entityId":612562,"collectionKey":"activities-for-update-priority","withPriority":true} {"correlation_id":"c1949c5e-c0f4-431e-ba26-59d49408d871","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:08] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {"activity":612562,"remote_search":true,"lead_id":null,"contact_id":null,"account_id":26,"opportunity_id":22,"stage_id":89} {"correlation_id":"c1949c5e-c0f4-431e-ba26-59d49408d871","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:09] local.INFO: [MatchActivityCrmData] Starting CRM data matching {"activity":612561,"remote_search":true,"set_configuration":2,"old_state":{"lead_id":null,"contact_id":null,"account_id":26,"opportunity_id":22,"stage_id":89}} {"correlation_id":"b715f531-04da-410f-be31-055aa1c13754","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:09] local.INFO: [ EsUpdateTarget ] Update single target {"target":"activities","purpose":"searchable-observer-update","entityId":612561} {"correlation_id":"b715f531-04da-410f-be31-055aa1c13754","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:09] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {"entityType":"activities","entityId":612561,"collectionKey":"activities-for-update-priority","withPriority":true} {"correlation_id":"b715f531-04da-410f-be31-055aa1c13754","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:09] local.INFO: [MatchActivityCrmData] Participants old state {"activity":612561,"participants":[{"id":999780,"user_id":206,"contact_id":null,"lead_id":null},{"id":999781,"user_id":null,"contact_id":null,"lead_id":null}]} {"correlation_id":"b715f531-04da-410f-be31-055aa1c13754","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:09] local.INFO: [SocialAccountService] Fetching token {"socialAccountId":1499,"provider":"hubspot"} {"correlation_id":"b715f531-04da-410f-be31-055aa1c13754","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:09] local.INFO: [SocialAccountService] Token retrieved {"socialAccountId":1499,"provider":"hubspot"} {"correlation_id":"b715f531-04da-410f-be31-055aa1c13754","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:09] local.INFO: [EncryptedTokenManager] Generating access token. {"mode":"legacy"} {"correlation_id":"b715f531-04da-410f-be31-055aa1c13754","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:09] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {"crm_provider":"hubspot","crm_owner":148,"team_id":2} {"correlation_id":"b715f531-04da-410f-be31-055aa1c13754","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:09] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {"activity_id":612561,"team_id":2,"email":"[EMAIL]"} {"correlation_id":"b715f531-04da-410f-be31-055aa1c13754","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:09] local.INFO: [Prospect match] Cache miss, calling the API {"identifier_type":"email","identifier":"[EMAIL]"} {"correlation_id":"b715f531-04da-410f-be31-055aa1c13754","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:10] local.INFO: [Hubspot] Failed to fetch contact {"email":"[EMAIL]","reason":"[404] Client error: `GET https://api.hubapi.com/crm/v3/objects/contacts/447700174614.447782589921.OeREojLVnk%40txt.staging.jiminny.com?properties=email%2Cfirstname%2Clastname%2Ccountry%2Cphone%2Cmobilephone%2Cjobtitle%2Chubspot_owner_id%2Cassociatedcompanyid%2Cphoto&archived=0&idProperty=email` resulted in a `404 Not Found` response"} {"correlation_id":"b715f531-04da-410f-be31-055aa1c13754","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:10] local.INFO: [Prospect match] API returned empty result, caching the miss with empty prospect data {"identifier_type":"email","identifier":"[EMAIL]"} {"correlation_id":"b715f531-04da-410f-be31-055aa1c13754","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:10] local.INFO: [Prospect match] Resolved company domain from email {"email":"[EMAIL]","domain":"jiminny.com"} {"correlation_id":"b715f531-04da-410f-be31-055aa1c13754","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:10] local.INFO: [CrmActivityService] CRM matching completed {"activity_id":612561,"participants_processed":2,"exact_matches":0,"domain_matches":1,"best_match_found":true} {"correlation_id":"b715f531-04da-410f-be31-055aa1c13754","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:10] local.INFO: [ EsUpdateTarget ] Update single target {"target":"activities","purpose":"searchable-observer-update","entityId":612561} {"correlation_id":"b715f531-04da-410f-be31-055aa1c13754","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:10] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {"entityType":"activities","entityId":612561,"collectionKey":"activities-for-update-priority","withPriority":true} {"correlation_id":"b715f531-04da-410f-be31-055aa1c13754","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:10] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {"activity":612561,"remote_search":true,"lead_id":null,"contact_id":null,"account_id":26,"opportunity_id":22,"stage_id":89} {"correlation_id":"b715f531-04da-410f-be31-055aa1c13754","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:10] local.INFO: [MatchActivityCrmData] Starting CRM data matching {"activity":612560,"remote_search":true,"set_configuration":2,"old_state":{"lead_id":null,"contact_id":null,"account_id":26,"opportunity_id":22,"stage_id":89}} {"correlation_id":"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:10] local.INFO: [ EsUpdateTarget ] Update single target {"target":"activities","purpose":"searchable-observer-update","entityId":612560} {"correlation_id":"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:10] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {"entityType":"activities","entityId":612560,"collectionKey":"activities-for-update-priority","withPriority":true} {"correlation_id":"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:10] local.INFO: [MatchActivityCrmData] Participants old state {"activity":612560,"participants":[{"id":999778,"user_id":null,"contact_id":null,"lead_id":null},{"id":999779,"user_id":206,"contact_id":null,"lead_id":null}]} {"correlation_id":"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [SocialAccountService] Fetching token {"socialAccountId":1499,"provider":"hubspot"} {"correlation_id":"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [SocialAccountService] Token retrieved {"socialAccountId":1499,"provider":"hubspot"} {"correlation_id":"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [EncryptedTokenManager] Generating access token. {"mode":"legacy"} {"correlation_id":"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {"crm_provider":"hubspot","crm_owner":148,"team_id":2} {"correlation_id":"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [Prospect match] Cache / local search hit {"identifier_type":"email","identifier":"[EMAIL]"} {"correlation_id":"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [Prospect match] cached empty result - no API calls, try next matching method {"identifier_type":"email","identifier":"[EMAIL]"} {"correlation_id":"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [Prospect match] Resolved company domain from email {"email":"[EMAIL]","domain":"jiminny.com"} {"correlation_id":"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {"activity_id":612560,"team_id":2,"email":"[EMAIL]"} {"correlation_id":"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [CrmActivityService] CRM matching completed {"activity_id":612560,"participants_processed":2,"exact_matches":0,"domain_matches":1,"best_match_found":true} {"correlation_id":"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [ EsUpdateTarget ] Update single target {"target":"activities","purpose":"searchable-observer-update","entityId":612560} {"correlation_id":"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [ EsUpdateProcessManager ] Finished updating entities in ES {"worker":"","peak_memory":"99.73 MB","elapsed_seconds":0.74,"update_target":"activities","should_iterate_again":false} {"correlation_id":"38935ae1-fcb3-437e-9c9e-1b7415a0b7e1","trace_id":"325a2f9b-9c66-4223-ac02-1a43723f4315"}
[2026-05-11 11:06:11] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {"entityType":"activities","entityId":612560,"collectionKey":"activities-for-update-priority","withPriority":true} {"correlation_id":"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {"activity":612560,"remote_search":true,"lead_id":null,"contact_id":null,"account_id":26,"opportunity_id":22,"stage_id":89} {"correlation_id":"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [MatchActivityCrmData] Starting CRM data matching {"activity":612559,"remote_search":true,"set_configuration":2,"old_state":{"lead_id":null,"contact_id":97,"account_id":69,"opportunity_id":5011,"stage_id":34}} {"correlation_id":"4d59a59c-38a5-4a1d-9379-6ceaf67b747e","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [ EsUpdateTarget ] Update single target {"target":"activities","purpose":"searchable-observer-update","entityId":612559} {"correlation_id":"4d59a59c-38a5-4a1d-9379-6ceaf67b747e","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {"entityType":"activities","entityId":612559,"collectionKey":"activities-for-update-priority","withPriority":true} {"correlation_id":"4d59a59c-38a5-4a1d-9379-6ceaf67b747e","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [MatchActivityCrmData] Participants old state {"activity":612559,"participants":[{"id":999776,"user_id":206,"contact_id":null,"lead_id":null},{"id":999777,"user_id":null,"contact_id":97,"lead_id":null}]} {"correlation_id":"4d59a59c-38a5-4a1d-9379-6ceaf67b747e","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [SocialAccountService] Fetching token {"socialAccountId":1499,"provider":"hubspot"} {"correlation_id":"4d59a59c-38a5-4a1d-9379-6ceaf67b747e","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [SocialAccountService] Token retrieved {"socialAccountId":1499,"provider":"hubspot"} {"correlation_id":"4d59a59c-38a5-4a1d-9379-6ceaf67b747e","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [EncryptedTokenManager] Generating access token. {"mode":"legacy"} {"correlation_id":"4d59a59c-38a5-4a1d-9379-6ceaf67b747e","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {"crm_provider":"hubspot","crm_owner":148,"team_id":2} {"correlation_id":"4d59a59c-38a5-4a1d-9379-6ceaf67b747e","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {"activity_id":612559,"team_id":2,"email":"[EMAIL]"} {"correlation_id":"4d59a59c-38a5-4a1d-9379-6ceaf67b747e","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: ProspectCache - Searching DB for opportunity by owner {"account_id":69,"contact_id":97,"owner_id":206} {"correlation_id":"4d59a59c-38a5-4a1d-9379-6ceaf67b747e","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: ProspectCache - Fallback DB opportunity search {"account_id":69,"contact_id":97} {"correlation_id":"4d59a59c-38a5-4a1d-9379-6ceaf67b747e","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: ProspectCache - Opportunity DB search results {"account_id":69,"contact_id":97,"opportunity_id":5011} {"correlation_id":"4d59a59c-38a5-4a1d-9379-6ceaf67b747e","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [Prospect match] Cache / local search hit {"identifier_type":"email","identifier":"[EMAIL]"} {"correlation_id":"4d59a59c-38a5-4a1d-9379-6ceaf67b747e","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [CrmActivityService] CRM matching completed {"activity_id":612559,"participants_processed":2,"exact_matches":1,"domain_matches":0,"best_match_found":true} {"correlation_id":"4d59a59c-38a5-4a1d-9379-6ceaf67b747e","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [ EsUpdateTarget ] Update single target {"target":"activities","purpose":"searchable-observer-update","entityId":612559} {"correlation_id":"4d59a59c-38a5-4a1d-9379-6ceaf67b747e","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {"entityType":"activities","entityId":612559,"collectionKey":"activities-for-update-priority","withPriority":true} {"correlation_id":"4d59a59c-38a5-4a1d-9379-6ceaf67b747e","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {"activity":612559,"remote_search":true,"lead_id":null,"contact_id":97,"account_id":69,"opportunity_id":5011,"stage_id":34} {"correlation_id":"4d59a59c-38a5-4a1d-9379-6ceaf67b747e","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:12] local.INFO: [MatchActivityCrmData] Starting CRM data matching {"activity":612558,"remote_search":true,"set_configuration":2,"old_state":{"lead_id":null,"contact_id":97,"account_id":69,"opportunity_id":5011,"stage_id":34}} {"correlation_id":"a105800d-d2f7-4772-a534-7791e262461c","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:12] local.INFO: [ EsUpdateTarget ] Update single target {"target":"activities","purpose":"searchable-observer-update","entityId":612558} {"correlation_id":"a105800d-d2f7-4772-a534-7791e262461c","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:12] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {"entityType":"activities","entityId":612558,"collectionKey":"activities-for-update-priority","withPriority":true} {"correlation_id":"a105800d-d2f7-4772-a534-7791e262461c","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:12] local.INFO: [MatchActivityCrmData] Participants old state {"activity":612558,"participants":[{"id":999774,"user_id":206,"contact_id":null,"lead_id":null},{"id":999775,"user_id":null,"contact_id":97,"lead_id":null}]} {"correlation_id":"a105800d-d2f7-4772-a534-7791e262461c","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:12] local.INFO: [SocialAccountService] Fetching token {"socialAccountId":1499,"provider":"hubspot"} {"correlation_id":"a105800d-d2f7-4772-a534-7791e262461c","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:12] local.INFO: [SocialAccountService] Token retrieved {"socialAccountId":1499,"provider":"hubspot"} {"correlation_id":"a105800d-d2f7-4772-a534-7791e262461c","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:12] local.INFO: [EncryptedTokenManager] Generating access token. {"mode":"legacy"} {"correlation_id":"a105800d-d2f7-4772-a534-7791e262461c","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:12] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {"crm_provider":"hubspot","crm_owner":148,"team_id":2} {"correlation_id":"a105800d-d2f7-4772-a534-7791e262461c","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:12] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {"activity_id":612558,"team_id":2,"email":"[EMAIL]"} {"correlation_id":"a105800d-d2f7-4772-a534-7791e262461c","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:12] local.INFO: [Prospect match] Cache / local search hit {"identifier_type":"email","identifier":"[EMAIL]"} {"correlation_id":"a105800d-d2f7-4772-a534-7791e262461c","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:12] local.INFO: [CrmActivityService] CRM matching completed {"activity_id":612558,"participants_processed":2,"exact_matches":1,"domain_matches":0,"best_match_found":true} {"correlation_id":"a105800d-d2f7-4772-a534-7791e262461c","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:12] local.INFO: [ EsUpdateTarget ] Update single target {"target":"activities","purpose":"searchable-observer-update","entityId":612558} {"correlation_id":"a105800d-d2f7-4772-a534-7791e262461c","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:12] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {"entityType":"activities","entityId":612558,"collectionKey":"activities-for-update-priority","withPriority":true} {"correlation_id":"a105800d-d2f7-4772-a534-7791e262461c","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:12] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {"activity":612558,"remote_search":true,"lead_id":null,"contact_id":97,"account_id":69,"opportunity_id":5011,"stage_id":34} {"correlation_id":"a105800d-d2f7-4772-a534-7791e262461c","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:13] local.INFO: [MatchActivityCrmData] Starting CRM data matching {"activity":612557,"remote_search":true,"set_configuration":2,"old_state":{"lead_id":null,"contact_id":97,"account_id":69,"opportunity_id":5011,"stage_id":34}} {"correlation_id":"3ec57437-5796-4e44-8bf0-aaec149ac813","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:13] local.INFO: [ EsUpdateTarget ] Update single target {"target":"activities","purpose":"searchable-observer-update","entityId":612557} {"correlation_id":"3ec57437-5796-4e44-8bf0-aaec149ac813","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:13] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {"entityType":"activities","entityId":612557,"collectionKey":"activities-for-update-priority","withPriority":true} {"correlation_id":"3ec57437-5796-4e44-8bf0-aaec149ac813","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:13] local.INFO: [MatchActivityCrmData] Participants old state {"activity":612557,"participants":[{"id":999772,"user_id":206,"contact_id":null,"lead_id":null},{"id":999773,"user_id":null,"contact_id":97,"lead_id":null}]} {"correlation_id":"3ec57437-5796-4e44-8bf0-aaec149ac813","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:13] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"mailbox:skip-lists:refresh","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.727} {"correlation_id":"5203040f-23be-43e6-8410-ba49ee1b38b4","trace_id":"e787953d-9b78-4b63-a987-4f93f6892984"}
[2026-05-11 11:06:13] local.INFO: [SocialAccountService] Fetching token {"socialAccountId":1499,"provider":"hubspot"} {"correlation_id":"3ec57437-5796-4e44-8bf0-aaec149ac813","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:13] local.INFO: [SocialAccountService] Token retrieved {"socialAccountId":1499,"provider":"hubspot"} {"correlation_id":"3ec57437-5796-4e44-8bf0-aaec149ac813","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:13] local.INFO: [EncryptedTokenManager] Generating access token. {"mode":"legacy"} {"correlation_id":"3ec57437-5796-4e44-8bf0-aaec149ac813","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:13] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {"crm_provider":"hubspot","crm_owner":148,"team_id":2} {"correlation_id":"3ec57437-5796-4e44-8bf0-aaec149ac813","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:13] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {"activity_id":612557,"team_id":2,"email":"[EMAIL]"} {"correlation_id":"3ec57437-5796-4e44-8bf0-aaec149ac813","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:13] local.INFO: [Prospect match] Cache / local search hit {"identifier_type":"email","identifier":"[EMAIL]"} {"correlation_id":"3ec57437-5796-4e44-8bf0-aaec149ac813","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:13] local.INFO: [CrmActivityService] CRM matching completed {"activity_id":612557,"participants_processed":2,"exact_matches":1,"domain_matches":0,"best_match_found":true} {"correlation_id":"3ec57437-5796-4e44-8bf0-aaec149ac813","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:13] local.INFO: [ EsUpdateTarget ] Update single target {"target":"activities","purpose":"searchable-observer-update","entityId":612557} {"correlation_id":"3ec57437-5796-4e44-8bf0-aaec149ac813","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:13] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {"entityType":"activities","entityId":612557,"collectionKey":"activities-for-update-priority","withPriority":true} {"correlation_id":"3ec57437-5796-4e44-8bf0-aaec149ac813","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:13] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {"activity":612557,"remote_search":true,"lead_id":null,"contact_id":97,"account_id":69,"opportunity_id":5011,"stage_id":34} {"correlation_id":"3ec57437-5796-4e44-8bf0-aaec149ac813","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:13] local.INFO: Jiminny\Console\Commands\Command::run Memory usage for command {"command":"mailbox:skip-lists:refresh","memoryBeforeCommandInMb":60.0,"memoryAfterCommandInMB":60.0,"memoryPeakBeforeCommandInMb":99.727,"memoryPeakAfterCommandInMB":99.727} {"correlation_id":"5203040f-23be-43e6-8410-ba49ee1b38b4","trace_id":"e787953d-9b78-4b63-a987-4f93f6892984"}
[2026-05-11 11:06:13] local.INFO: [MatchActivityCrmData] Starting CRM data matching {"activity":612556,"remote_search":true,"set_configuration":2,"old_state":{"lead_id":null,"contact_id":97,"account_id":69,"opportunity_id":5011,"stage_id":34}} {"correlation_id":"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:13] local.INFO: [ EsUpdateTarget ] Update single target {"target":"activities","purpose":"searchable-observer-update","entityId":612556} {"correlation_id":"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:13] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {"entityType":"activities","entityId":612556,"collectionKey":"activities-for-update-priority","withPriority":true} {"correlation_id":"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:13] local.INFO: [MatchActivityCrmData] Participants old state {"activity":612556,"participants":[{"id":999770,"user_id":206,"contact_id":null,"lead_id":null},{"id":999771,"user_id":null,"contact_id":97,"lead_id":null}]} {"correlation_id":"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:13] local.INFO: [SocialAccountService] Fetching token {"socialAccountId":1499,"provider":"hubspot"} {"correlation_id":"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:13] local.INFO: [SocialAccountService] Token retrieved {"socialAccountId":1499,"provider":"hubspot"} {"correlation_id":"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:13] local.INFO: [EncryptedTokenManager] Generating access token. {"mode":"legacy"} {"correlation_id":"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:13] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {"crm_provider":"hubspot","crm_owner":148,"team_id":2} {"correlation_id":"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:14] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {"activity_id":612556,"team_id":2,"email":"[EMAIL]"} {"correlation_id":"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:14] local.INFO: [Prospect match] Cache / local search hit {"identifier_type":"email","identifier":"[EMAIL]"} {"correlation_id":"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:14] local.INFO: [CrmActivityService] CRM matching completed {"activity_id":612556,"participants_processed":2,"exact_matches":1,"domain_matches":0,"best_match_found":true} {"correlation_id":"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:14] local.INFO: [ EsUpdateTarget ] Update single target {"target":"activities","purpose":"searchable-observer-update","entityId":612556} {"correlation_id":"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:14] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {"entityType":"activities","entityId":612556,"collectionKey":"activities-for-update-priority","withPriority":true} {"correlation_id":"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:14] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {"activity":612556,"remote_search":true,"lead_id":null,"contact_id":97,"account_id":69,"opportunity_id":5011,"stage_id":34} {"correlation_id":"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:14] local.INFO: [MatchActivityCrmData] Starting CRM data matching {"activity":612555,"remote_search":true,"set_configuration":2,"old_state":{"lead_id":null,"contact_id":97,"account_id":69,"opportunity_id":5011,"stage_id":34}} {"correlation_id":"3e9d1a70-5491-44ae-8f28-5cb9b60e1859","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:14] local.INFO: [ EsUpdateTarget ] Update single target {"target":"activities","purpose":"searchable-observer-update","entityId":612555} {"correlation_id":"3e9d1a70-5491-44ae-8f28-5cb9b60e1859","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:14] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {"entityType":"activities","entityId":612555,"collectionKey":"activities-for-update-priority","withPriority":true} {"correlation_id":"3e9d1a70-5491-44ae-8f28-5cb9b60e1859","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:14] local.INFO: [MatchActivityCrmData] Participants old state {"activity":612555,"participants":[{"id":999768,"user_id":206,"contact_id":null,"lead_id":null},{"id":999769,"user_id":null,"contact_id":97,"lead_id":null}]} {"correlation_id":"3e9d1a70-5491-44ae-8f28-5cb9b60e1859","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:14] local.INFO: [SocialAccountService] Fetching token {"socialAccountId":1499,"provider":"hubspot"} {"correlation_id":"3e9d1a70-5491-44ae-8f28-5cb9b60e1859","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:14] local.INFO: [SocialAccountService] Token retrieved {"socialAccountId":1499,"provider":"hubspot"} {"correlation_id":"3e9d1a70-5491-44ae-8f28-5cb9b60e1859","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:14] local.INFO: [EncryptedTokenManager] Generating access token. {"mode":"legacy"} {"correlation_id":"3e9d1a70-5491-44ae-8f28-5cb9b60e1859","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:14] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {"crm_provider":"hubspot","crm_owner":148,"team_id":2} {"correlation_id":"3e9d1a70-5491-44ae-8f28-5cb9b60e1859","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:14] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {"activity_id":612555,"team_id":2,"email":"[EMAIL]"} {"correlation_id":"3e9d1a70-5491-44ae-8f28-5cb9b60e1859","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:14] local.INFO: [Prospect match] Cache / local search hit {"identifier_type":"email","identifier":"[EMAIL]"} {"correlation_id":"3e9d1a70-5491-44ae-8f28-5cb9b60e1859","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:14] local.INFO: [CrmActivityService] CRM matching completed {"activity_id":612555,"participants_processed":2,"exact_matches":1,"domain_matches":0,"best_match_found":true} {"correlation_id":"3e9d1a70-5491-44ae-8f28-5cb9b60e1859","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:14] local.INFO: [ EsUpdateTarget ] Update single target {"target":"activities","purpose":"searchable-observer-update","entityId":612555} {"correlation_id":"3e9d1a70-5491-44ae-8f28-5cb9b60e1859","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:14] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {"entityType":"activities","entityId":612555,"collectionKey":"activities-for-update-priority","withPriority":true} {"correlation_id":"3e9d1a70-5491-44ae-8f28-5cb9b60e1859","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:14] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {"activity":612555,"remote_search":true,"lead_id":null,"contact_id":97,"account_id":69,"opportunity_id":5011,"stage_id":34} {"correlation_id":"3e9d1a70-5491-44ae-8f28-5cb9b60e1859","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:14] local.INFO: [MatchActivityCrmData] Starting CRM data matching {"activity":612554,"remote_search":true,"set_configuration":2,"old_state":{"lead_id":null,"contact_id":97,"account_id":69,"opportunity_id":5011,"stage_id":34}} {"correlation_id":"7101e979-3a7e-4b2c-bf04-dc6a24ba1356","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:14] local.INFO: [ EsUpdateTarget ] Update single target {"target":"activities","purpose":"searchable-observer-update","entityId":612554} {"correlation_id":"7101e979-3a7e-4b2c-bf04-dc6a24ba1356","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:14] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {"entityType":"activities","entityId":612554,"collectionKey":"activities-for-update-priority",...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
18247
|
791
|
30
|
2026-05-11T11:06:53.221519+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-11/1778 /Users/lukas/.screenpipe/data/data/2026-05-11/1778497613221_m2.jpg...
|
PhpStorm
|
faVsco.js – laravel.log
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
JY-20725-handle-HS-search Project: faVsco.js, menu
JY-20725-handle-HS-search-rate-limit, menu
Start Listening for PHP Debug Connections
HandleHubspotRateLimitTest
Run 'HandleHubspotRateLimitTest'
Debug 'HandleHubspotRateLimitTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
5
133
11
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Console\Commands;
use Carbon\Carbon;
use Carbon\CarbonImmutable;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Redis;
use InvalidArgumentException;
use Jiminny\Jobs\AutomatedReports\RequestGenerateAskJiminnyReportJob;
use Jiminny\Jobs\AutomatedReports\SendReportMailJob;
use Jiminny\Jobs\Crm\Delete\VerifyActivityCrmTaskJob;
use Jiminny\Jobs\Crm\MatchActivityCrmData;
use Jiminny\Jobs\JobDispatcherInterface;
use Jiminny\Models\Activity;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\AutomatedReportResult;
use Jiminny\Models\Team;
use Jiminny\Models\User;
use Jiminny\Repositories\AutomatedReportsRepository;
use Jiminny\Services\Activity\CrmOwnerResolver;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
/**
* Class JiminnyDebugCommand
*
* @package Jiminny\Console\Commands
*/
class JiminnyDebugCommand extends Command
{
public const string FREQUENCY_DAILY = 'daily';
public const string FREQUENCY_WEEKLY = 'weekly';
public const string FREQUENCY_MONTHLY = 'monthly';
public const string FREQUENCY_QUARTERLY = 'quarterly';
public const string FREQUENCY_ONE_OFF = 'one_off';
protected $signature = 'jiminny:debug';
public function handle(
JobDispatcherInterface $jobDispatcher,
AutomatedReportsService $automatedReportsService,
AutomatedReportsRepository $automatedReportsRepository,
UserPilotClient $userPilotClient
): void {
// Choose ONE of the following to run, then comment out the others.
// 1) Dispatch a storm of MatchActivityCrmData jobs against team 2
$this->simulateMatchActivityStorm(teamId: 2, count: 100);
// 2) Dispatch a storm of VerifyActivityCrmTaskJob jobs (simulates DeleteCrmEntityTrait fan-out)
// $this->simulateVerifyTaskStorm(teamId: 2, count: 100);
// 3) Inspect Redis circuit-breaker state for the team's HubSpot portal
// $this->observeRateLimitCache(teamId: 2);
// 4) Make 3 synchronous matchByName calls (foreground, hits API directly)
// $this->rateLimit();
exit(1);
$report = AutomatedReport::find(71);
$last = AutomatedReportResult::query()
->where('report_id', $report->getId())
->whereIn('status', [AutomatedReportResult::STATUS_DEFAULT, AutomatedReportResult::STATUS_FAILED])
// ->where('reason', '!=', AutomatedReportResult::REASON_NOT_ENOUGH_ACTIVITIES)
->whereDate('created_at', CarbonImmutable::now()->toDateString())
->latest()
->first();
$this->info("Last: {$last->getId()}");
exit(1);
$user = User::find(143);
// $count = $automatedReportsRepository->countUserReports($user);
// $this->info("Count: {$count}");
// $count = $automatedReportsRepository->countAllUserReports($user);
// $this->info("All count: {$count}");
$payload = [
'report_type' => 'ask_jiminny',
'frequency' => 'weekly',
];
$userPilotClient->track($user, 'ask-jiminny-report-generated', $payload);
exit(1);
$now = Carbon::now()->subDay(1);
$this->info("Now: {$now->toDateTimeString()}");
$weekStart = Carbon::getWeekStartsAt();
$this->info("Now: {$weekStart}");
// $from = $now->copy()->previousWeekday()->startOfDay();
// $to = $now->copy()->previousWeekday()->endOfDay();
// $fromOld = $now->copy()->subWeeks(1)->startOfDay();
// $toOld = $now->copy()->subDay()->endOfDay();
// $fromNew = $now->copy()->subWeek()->startOfWeek();
// $toNew = $now->copy()->subWeek()->endOfWeek();
// $fromOld = $now->copy()->subMonths(1)->startOfDay();
// $toOld = $now->copy()->subDay()->endOfDay();
// $fromNew = $now->copy()->subMonthNoOverflow()->startOfMonth();
// $toNew = $now->copy()->subMonthNoOverflow()->endOfMonth();
$fromOld = $now->copy()->subMonths(3)->startOfDay();
$toOld = $now->copy()->subDay()->endOfDay();
$fromNew = $now->copy()->subQuarterNoOverflow()->startOfQuarter();
$toNew = $now->copy()->subQuarterNoOverflow()->endOfQuarter();
$this->info("From old: {$fromOld->toDateTimeString()}");
$this->info("To old: {$toOld->toDateTimeString()}");
$this->info("From new: {$fromNew->toDateTimeString()}");
$this->info("To new: {$toNew->toDateTimeString()}");
exit(1);
$report = AutomatedReport::find(71);
$job = new RequestGenerateAskJiminnyReportJob($report->getUuid());
$jobDispatcher->dispatch($job);
exit(1);
// $this->formatDate($jobDispatcher);
// $this->sendMail($jobDispatcher, $automatedReportsService);
// $this->crmService();
$this->getPayload($automatedReportsService);
exit(1);
}
private function crmService()
{
$activity = Activity::find(418141);
$team = Team::find(19);
$config = $team->getCrmConfiguration();
$crmResolver = app(CrmOwnerResolver::class, [
'team' => $team,
'integrationAdmin' => $team->getOwner(),
'providerSlug' => $config->getProviderName(),
]);
$crmService = $crmResolver->prepareCrmService();
$crmService->createTranscriptNotes($activity);
}
private function sendMail(JobDispatcherInterface $jobDispatcher, AutomatedReportsService $automatedReportsService)
{
$reportUuid = '';
// $report = $automatedReportsService->getReportResult($reportUuid);
$report = AutomatedReportResult::find(275);
$validRecipients = $automatedReportsService->getValidRecipientUsers(
$report->getReport(),
includeJiminny: true,
);
$recipient = $validRecipients[0];
$fileName = $automatedReportsService->getReportFileName($report);
$typeName = $report->getReport()->getCustomName()
?? $automatedReportsService->getReportTypeName($report);
$teamsName = $automatedReportsService->getReportTeamsName($report);
$periodName = $automatedReportsService->getReportPeriodName($report);
$s3Path = $automatedReportsService->getMediaPath($report);
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$fileName ' . PHP_EOL . print_r($fileName, true));
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$typeName ' . PHP_EOL . print_r($typeName, true));
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$teamsName ' . PHP_EOL . print_r($teamsName, true));
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$periodName ' . PHP_EOL . print_r($periodName, true));
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$s3Path ' . PHP_EOL . print_r($s3Path, true));
$jobDispatcher->dispatch(
new SendReportMailJob(
reportUuid: $report->getUuid(),
s3Path: $s3Path,
recipientEmail: $recipient['email'],
recipientName: $recipient['name'] ?? null,
fileName: $fileName,
typeName: $typeName,
teamsName: $teamsName,
periodName: $periodName,
isAskJiminny: true,
)
);
exit(1);
}
private function formatDate(JobDispatcherInterface $jobDispatcher): void
{
$customName = 'Custom report name';
// $frequency = self::FREQUENCY_DAILY;
// $frequency = self::FREQUENCY_WEEKLY;
$frequency = self::FREQUENCY_MONTHLY;
// $frequency = self::FREQUENCY_QUARTERLY;
// $frequency = self::FREQUENCY_ONE_OFF;
$period = $this->calculateFromAndToDatePeriod($frequency);
$from = $period['fromDate'];
$to = $period['toDate'];
$periodName = $this->formatReportPeriodName($frequency, $from, $to);
$filenameSuffix = null;
if ($customName) {
if ($filenameSuffix) {
$customName .= " {$filenameSuffix}";
}
$result = $this->sanitizeFileName("{$customName} - {$periodName}");
}
$this->info($result);
}
public function calculateFromAndToDatePeriod(
string $frequency,
?Carbon $fromDate = null,
?Carbon $toDate = null
): array {
if ($frequency === self::FREQUENCY_ONE_OFF) {
return [
'fromDate' => $fromDate,
'toDate' => $toDate,
];
}
$now = Carbon::now();
return match ($frequency) {
self::FREQUENCY_DAILY => [
'fromDate' => $now->copy()->subDay()->startOfDay(),
'toDate' => $now->copy()->subDay()->endOfDay(),
],
self::FREQUENCY_WEEKLY => [
'fromDate' => $now->copy()->subWeeks(1)->startOfDay(),
'toDate' => $now->copy()->subDay()->endOfDay(),
],
self::FREQUENCY_MONTHLY => [
'fromDate' => $now->copy()->subMonths(1)->startOfDay(),
'toDate' => $now->copy()->subDay()->endOfDay(),
],
self::FREQUENCY_QUARTERLY => [
'fromDate' => $now->copy()->subMonths(3)->startOfDay(),
'toDate' => $now->copy()->subDay()->endOfDay(),
],
default => throw new InvalidArgumentException("Unsupported frequency: {$frequency}"),
};
}
private function formatReportPeriodName(string $frequency, Carbon $from, Carbon $to): string
{
$fromYear = $from->format('Y');
$toYear = $to->format('Y');
$differentYears = $fromYear !== $toYear;
switch ($frequency) {
case self::FREQUENCY_DAILY:
return $from->format('j M Y');
case self::FREQUENCY_QUARTERLY:
// 'Jan-Mar 2025' or 'Nov 2024-Jan 2025' if years differ
$startMonth = $from->format('M');
$endMonth = $to->copy()->subMonth();
$endMonthName = $endMonth->format('M');
$endMonthYear = $endMonth->format('Y');
if ($differentYears) {
return "{$startMonth} {$fromYear} - {$endMonthName} {$endMonthYear}";
}
return "{$startMonth} - {$endMonthName} {$toYear}";
case self::FREQUENCY_MONTHLY:
// 'May 2025' - monthly reports are always within the same year
return $from->format('M Y');
case self::FREQUENCY_WEEKLY:
// '4 - 8 Aug 2025', '27 Oct - 3 Nov 2025', or '28 Dec 2024 - 3 Jan 2025' if years differ
$startDay = $from->format('j');
$endDay = $to->format('j');
$startMonth = $from->format('M');
$endMonth = $to->format('M');
if ($differentYears) {
return "{$startDay} {$startMonth} {$fromYear} - {$endDay} {$endMonth} {$toYear}";
}
if ($startMonth !== $endMonth) {
return "{$startDay} {$startMonth} - {$endDay} {$endMonth} {$toYear}";
}
return "{$startDay} - {$endDay} {$endMonth} {$toYear}";
case self::FREQUENCY_ONE_OFF:
// '2 May-31 May 2025' or '15 Dec 2024-15 Jan 2025' if years differ
$startDay = $from->format('j');
$startMonth = $from->format('M');
$endDay = $to->format('j');
$endMonth = $to->format('M');
// If same month and year, use a format like '2-31 May 2025'
if ($startMonth === $endMonth && ! $differentYears) {
return "{$startDay} - {$endDay} {$startMonth} {$toYear}";
}
// If different years, include both years
if ($differentYears) {
return "{$startDay} {$startMonth} {$fromYear} - {$endDay} {$endMonth} {$toYear}";
}
// Same year but different months
return "{$startDay} {$startMonth} - {$endDay} {$endMonth} {$toYear}";
default:
// Default format for unknown frequencies
return $from->format('j M Y') . ' - ' . $to->format('j M Y');
}
}
public function sanitizeFileName(string $fileName): string
{
return str_replace(['/', '\\'], '-', $fileName);
}
private function getPayload(AutomatedReportsService $automatedReportsService)
{
$reportResult = AutomatedReportResult::find(269);
$automatedReport = $reportResult->getReport();
$activityIds = [1,2,3];
$payload = $automatedReportsService->getAskJiminnyGenerateReportPayload(
automatedReport: $automatedReport,
reportResult: $reportResult,
activityIds: $activityIds,
);
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$payload ' . PHP_EOL . print_r($payload, true));
}
private function rateLimit()
{
$team = Team::find(2);
$config = $team->getCrmConfiguration();
$crmResolver = app(CrmOwnerResolver::class, [
'team' => $team,
'integrationAdmin' => $team->getOwner(),
'providerSlug' => $config->getProviderName(),
]);
$crmService = $crmResolver->prepareCrmService();
for ($i = 0 ; $i < 3; $i++) {
// if ($i % 25 === 0) {
// $this->info("Syncing opportunity {$i}");
$this->info("Matching contact {$i}");
// }
// $crmService->syncOpportunity('374720564');
$crmService->matchByName('Robot');
}
}
private function simulateMatchActivityStorm(int $teamId = 2, int $count = 100): void
{
$team = Team::find($teamId);
$config = $team->getCrmConfiguration();
$activities = Activity::query()
->where('crm_configuration_id', $config->getId())
->orderByDesc('id')
->limit($count)
->get();
$this->info("Dispatching {$activities->count()} MatchActivityCrmData jobs (portal={$config->getId()})");
foreach ($activities as $activity) {
MatchActivityCrmData::dispatch($activity->getId(), $config, true);
}
$this->info('Done. Watch logs and run jiminny:debug observeRateLimit to inspect cache state.');
}
private function simulateVerifyTaskStorm(int $teamId = 2, int $count = 100): void
{
$activities = Activity::query()
->where('team_id', $teamId)
->whereNotNull('crm_provider_id')
->orderByDesc('id')
->limit($count)
->get();
$this->info("Dispatching {$activities->count()} VerifyActivityCrmTaskJob jobs");
foreach ($activities as $activity) {
VerifyActivityCrmTaskJob::dispatch($activity->getId());
}
$this->info('Done.');
}
private function observeRateLimitCache(int $teamId = 2): void
{
$team = Team::find($teamId);
$config = $team->getCrmConfiguration();
$key = sprintf('hubspot:ratelimit:portal:%d', $config->getId());
$value = Redis::get($key);
$ttl = Redis::ttl($key);
$this->info("Redis key: {$key}");
$this->info('Value: ' . ($value ?? '(empty)'));
$this->info("TTL: {$ttl}s");
}
}
Show Replace Field
Search History
429
New Line
Match Case
Words
Regex
Replace History
Replace
New Line
Preserve case...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"bounds":{"left":0.025930852,"top":0.019952115,"width":0.03856383,"height":0.025538707},"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JY-20725-handle-HS-search-rate-limit, menu","depth":5,"bounds":{"left":0.064494684,"top":0.019952115,"width":0.09541223,"height":0.025538707},"on_screen":true,"help_text":"Git Branch: JY-20725-handle-HS-search-rate-limit","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"bounds":{"left":0.82413566,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"HandleHubspotRateLimitTest","depth":6,"bounds":{"left":0.8394282,"top":0.019952115,"width":0.076130316,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'HandleHubspotRateLimitTest'","depth":6,"bounds":{"left":0.9155585,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'HandleHubspotRateLimitTest'","depth":6,"bounds":{"left":0.9268617,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"bounds":{"left":0.9381649,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"bounds":{"left":0.96609044,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"bounds":{"left":0.9773936,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"bounds":{"left":0.9886968,"top":0.019952115,"width":0.011303186,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"5","depth":4,"bounds":{"left":0.5043218,"top":0.17478053,"width":0.007978723,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"133","depth":4,"bounds":{"left":0.5142952,"top":0.17478053,"width":0.011968086,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"11","depth":4,"bounds":{"left":0.52825797,"top":0.17478053,"width":0.008976064,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.53889626,"top":0.17318435,"width":0.00731383,"height":0.018355945},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.5462101,"top":0.17318435,"width":0.006981383,"height":0.018355945},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Console\\Commands;\n\nuse Carbon\\Carbon;\nuse Carbon\\CarbonImmutable;\nuse Illuminate\\Console\\Command;\nuse Illuminate\\Support\\Facades\\Redis;\nuse InvalidArgumentException;\nuse Jiminny\\Jobs\\AutomatedReports\\RequestGenerateAskJiminnyReportJob;\nuse Jiminny\\Jobs\\AutomatedReports\\SendReportMailJob;\nuse Jiminny\\Jobs\\Crm\\Delete\\VerifyActivityCrmTaskJob;\nuse Jiminny\\Jobs\\Crm\\MatchActivityCrmData;\nuse Jiminny\\Jobs\\JobDispatcherInterface;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\AutomatedReportResult;\nuse Jiminny\\Models\\Team;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Repositories\\AutomatedReportsRepository;\nuse Jiminny\\Services\\Activity\\CrmOwnerResolver;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\n\n/**\n * Class JiminnyDebugCommand\n *\n * @package Jiminny\\Console\\Commands\n */\nclass JiminnyDebugCommand extends Command\n{\n public const string FREQUENCY_DAILY = 'daily';\n public const string FREQUENCY_WEEKLY = 'weekly';\n public const string FREQUENCY_MONTHLY = 'monthly';\n public const string FREQUENCY_QUARTERLY = 'quarterly';\n public const string FREQUENCY_ONE_OFF = 'one_off';\n protected $signature = 'jiminny:debug';\n\n public function handle(\n JobDispatcherInterface $jobDispatcher,\n AutomatedReportsService $automatedReportsService,\n AutomatedReportsRepository $automatedReportsRepository,\n UserPilotClient $userPilotClient\n ): void {\n // Choose ONE of the following to run, then comment out the others.\n // 1) Dispatch a storm of MatchActivityCrmData jobs against team 2\n $this->simulateMatchActivityStorm(teamId: 2, count: 100);\n\n // 2) Dispatch a storm of VerifyActivityCrmTaskJob jobs (simulates DeleteCrmEntityTrait fan-out)\n // $this->simulateVerifyTaskStorm(teamId: 2, count: 100);\n\n // 3) Inspect Redis circuit-breaker state for the team's HubSpot portal\n // $this->observeRateLimitCache(teamId: 2);\n\n // 4) Make 3 synchronous matchByName calls (foreground, hits API directly)\n // $this->rateLimit();\n exit(1);\n\n\n\n $report = AutomatedReport::find(71);\n $last = AutomatedReportResult::query()\n ->where('report_id', $report->getId())\n ->whereIn('status', [AutomatedReportResult::STATUS_DEFAULT, AutomatedReportResult::STATUS_FAILED])\n// ->where('reason', '!=', AutomatedReportResult::REASON_NOT_ENOUGH_ACTIVITIES)\n ->whereDate('created_at', CarbonImmutable::now()->toDateString())\n ->latest()\n ->first();\n\n $this->info(\"Last: {$last->getId()}\");\n\n exit(1);\n\n $user = User::find(143);\n // $count = $automatedReportsRepository->countUserReports($user);\n // $this->info(\"Count: {$count}\");\n // $count = $automatedReportsRepository->countAllUserReports($user);\n // $this->info(\"All count: {$count}\");\n\n $payload = [\n 'report_type' => 'ask_jiminny',\n 'frequency' => 'weekly',\n ];\n $userPilotClient->track($user, 'ask-jiminny-report-generated', $payload);\n\n exit(1);\n\n $now = Carbon::now()->subDay(1);\n $this->info(\"Now: {$now->toDateTimeString()}\");\n $weekStart = Carbon::getWeekStartsAt();\n $this->info(\"Now: {$weekStart}\");\n\n // $from = $now->copy()->previousWeekday()->startOfDay();\n // $to = $now->copy()->previousWeekday()->endOfDay();\n\n // $fromOld = $now->copy()->subWeeks(1)->startOfDay();\n // $toOld = $now->copy()->subDay()->endOfDay();\n // $fromNew = $now->copy()->subWeek()->startOfWeek();\n // $toNew = $now->copy()->subWeek()->endOfWeek();\n\n // $fromOld = $now->copy()->subMonths(1)->startOfDay();\n // $toOld = $now->copy()->subDay()->endOfDay();\n // $fromNew = $now->copy()->subMonthNoOverflow()->startOfMonth();\n // $toNew = $now->copy()->subMonthNoOverflow()->endOfMonth();\n\n $fromOld = $now->copy()->subMonths(3)->startOfDay();\n $toOld = $now->copy()->subDay()->endOfDay();\n $fromNew = $now->copy()->subQuarterNoOverflow()->startOfQuarter();\n $toNew = $now->copy()->subQuarterNoOverflow()->endOfQuarter();\n\n $this->info(\"From old: {$fromOld->toDateTimeString()}\");\n $this->info(\"To old: {$toOld->toDateTimeString()}\");\n $this->info(\"From new: {$fromNew->toDateTimeString()}\");\n $this->info(\"To new: {$toNew->toDateTimeString()}\");\n\n exit(1);\n\n $report = AutomatedReport::find(71);\n\n $job = new RequestGenerateAskJiminnyReportJob($report->getUuid());\n $jobDispatcher->dispatch($job);\n\n exit(1);\n\n\n // $this->formatDate($jobDispatcher);\n // $this->sendMail($jobDispatcher, $automatedReportsService);\n // $this->crmService();\n\n $this->getPayload($automatedReportsService);\n\n exit(1);\n }\n\n\n\n private function crmService()\n {\n $activity = Activity::find(418141);\n\n $team = Team::find(19);\n $config = $team->getCrmConfiguration();\n\n $crmResolver = app(CrmOwnerResolver::class, [\n 'team' => $team,\n 'integrationAdmin' => $team->getOwner(),\n 'providerSlug' => $config->getProviderName(),\n ]);\n\n $crmService = $crmResolver->prepareCrmService();\n\n $crmService->createTranscriptNotes($activity);\n }\n\n private function sendMail(JobDispatcherInterface $jobDispatcher, AutomatedReportsService $automatedReportsService)\n {\n $reportUuid = '';\n // $report = $automatedReportsService->getReportResult($reportUuid);\n $report = AutomatedReportResult::find(275);\n $validRecipients = $automatedReportsService->getValidRecipientUsers(\n $report->getReport(),\n includeJiminny: true,\n );\n\n $recipient = $validRecipients[0];\n\n $fileName = $automatedReportsService->getReportFileName($report);\n $typeName = $report->getReport()->getCustomName()\n ?? $automatedReportsService->getReportTypeName($report);\n $teamsName = $automatedReportsService->getReportTeamsName($report);\n $periodName = $automatedReportsService->getReportPeriodName($report);\n $s3Path = $automatedReportsService->getMediaPath($report);\n\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$fileName ' . PHP_EOL . print_r($fileName, true));\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$typeName ' . PHP_EOL . print_r($typeName, true));\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$teamsName ' . PHP_EOL . print_r($teamsName, true));\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$periodName ' . PHP_EOL . print_r($periodName, true));\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$s3Path ' . PHP_EOL . print_r($s3Path, true));\n\n $jobDispatcher->dispatch(\n new SendReportMailJob(\n reportUuid: $report->getUuid(),\n s3Path: $s3Path,\n recipientEmail: $recipient['email'],\n recipientName: $recipient['name'] ?? null,\n fileName: $fileName,\n typeName: $typeName,\n teamsName: $teamsName,\n periodName: $periodName,\n isAskJiminny: true,\n )\n );\n\n exit(1);\n }\n\n private function formatDate(JobDispatcherInterface $jobDispatcher): void\n {\n $customName = 'Custom report name';\n // $frequency = self::FREQUENCY_DAILY;\n // $frequency = self::FREQUENCY_WEEKLY;\n $frequency = self::FREQUENCY_MONTHLY;\n // $frequency = self::FREQUENCY_QUARTERLY;\n // $frequency = self::FREQUENCY_ONE_OFF;\n $period = $this->calculateFromAndToDatePeriod($frequency);\n $from = $period['fromDate'];\n $to = $period['toDate'];\n $periodName = $this->formatReportPeriodName($frequency, $from, $to);\n $filenameSuffix = null;\n\n if ($customName) {\n if ($filenameSuffix) {\n $customName .= \" {$filenameSuffix}\";\n }\n\n $result = $this->sanitizeFileName(\"{$customName} - {$periodName}\");\n }\n\n $this->info($result);\n }\n\n public function calculateFromAndToDatePeriod(\n string $frequency,\n ?Carbon $fromDate = null,\n ?Carbon $toDate = null\n ): array {\n if ($frequency === self::FREQUENCY_ONE_OFF) {\n return [\n 'fromDate' => $fromDate,\n 'toDate' => $toDate,\n ];\n }\n\n $now = Carbon::now();\n\n return match ($frequency) {\n self::FREQUENCY_DAILY => [\n 'fromDate' => $now->copy()->subDay()->startOfDay(),\n 'toDate' => $now->copy()->subDay()->endOfDay(),\n ],\n self::FREQUENCY_WEEKLY => [\n 'fromDate' => $now->copy()->subWeeks(1)->startOfDay(),\n 'toDate' => $now->copy()->subDay()->endOfDay(),\n ],\n self::FREQUENCY_MONTHLY => [\n 'fromDate' => $now->copy()->subMonths(1)->startOfDay(),\n 'toDate' => $now->copy()->subDay()->endOfDay(),\n ],\n self::FREQUENCY_QUARTERLY => [\n 'fromDate' => $now->copy()->subMonths(3)->startOfDay(),\n 'toDate' => $now->copy()->subDay()->endOfDay(),\n ],\n default => throw new InvalidArgumentException(\"Unsupported frequency: {$frequency}\"),\n };\n }\n\n private function formatReportPeriodName(string $frequency, Carbon $from, Carbon $to): string\n {\n $fromYear = $from->format('Y');\n $toYear = $to->format('Y');\n $differentYears = $fromYear !== $toYear;\n\n switch ($frequency) {\n case self::FREQUENCY_DAILY:\n return $from->format('j M Y');\n\n case self::FREQUENCY_QUARTERLY:\n // 'Jan-Mar 2025' or 'Nov 2024-Jan 2025' if years differ\n $startMonth = $from->format('M');\n $endMonth = $to->copy()->subMonth();\n $endMonthName = $endMonth->format('M');\n $endMonthYear = $endMonth->format('Y');\n\n if ($differentYears) {\n return \"{$startMonth} {$fromYear} - {$endMonthName} {$endMonthYear}\";\n }\n\n return \"{$startMonth} - {$endMonthName} {$toYear}\";\n\n case self::FREQUENCY_MONTHLY:\n // 'May 2025' - monthly reports are always within the same year\n return $from->format('M Y');\n\n case self::FREQUENCY_WEEKLY:\n // '4 - 8 Aug 2025', '27 Oct - 3 Nov 2025', or '28 Dec 2024 - 3 Jan 2025' if years differ\n $startDay = $from->format('j');\n $endDay = $to->format('j');\n $startMonth = $from->format('M');\n $endMonth = $to->format('M');\n\n if ($differentYears) {\n return \"{$startDay} {$startMonth} {$fromYear} - {$endDay} {$endMonth} {$toYear}\";\n }\n\n if ($startMonth !== $endMonth) {\n return \"{$startDay} {$startMonth} - {$endDay} {$endMonth} {$toYear}\";\n }\n\n return \"{$startDay} - {$endDay} {$endMonth} {$toYear}\";\n\n case self::FREQUENCY_ONE_OFF:\n // '2 May-31 May 2025' or '15 Dec 2024-15 Jan 2025' if years differ\n $startDay = $from->format('j');\n $startMonth = $from->format('M');\n $endDay = $to->format('j');\n $endMonth = $to->format('M');\n\n // If same month and year, use a format like '2-31 May 2025'\n if ($startMonth === $endMonth && ! $differentYears) {\n return \"{$startDay} - {$endDay} {$startMonth} {$toYear}\";\n }\n\n // If different years, include both years\n if ($differentYears) {\n return \"{$startDay} {$startMonth} {$fromYear} - {$endDay} {$endMonth} {$toYear}\";\n }\n\n // Same year but different months\n return \"{$startDay} {$startMonth} - {$endDay} {$endMonth} {$toYear}\";\n\n default:\n // Default format for unknown frequencies\n return $from->format('j M Y') . ' - ' . $to->format('j M Y');\n }\n }\n\n public function sanitizeFileName(string $fileName): string\n {\n return str_replace(['/', '\\\\'], '-', $fileName);\n }\n\n private function getPayload(AutomatedReportsService $automatedReportsService)\n {\n $reportResult = AutomatedReportResult::find(269);\n $automatedReport = $reportResult->getReport();\n $activityIds = [1,2,3];\n $payload = $automatedReportsService->getAskJiminnyGenerateReportPayload(\n automatedReport: $automatedReport,\n reportResult: $reportResult,\n activityIds: $activityIds,\n );\n\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$payload ' . PHP_EOL . print_r($payload, true));\n }\n\n private function rateLimit()\n {\n $team = Team::find(2);\n $config = $team->getCrmConfiguration();\n\n $crmResolver = app(CrmOwnerResolver::class, [\n 'team' => $team,\n 'integrationAdmin' => $team->getOwner(),\n 'providerSlug' => $config->getProviderName(),\n ]);\n\n $crmService = $crmResolver->prepareCrmService();\n\n for ($i = 0 ; $i < 3; $i++) {\n// if ($i % 25 === 0) {\n// $this->info(\"Syncing opportunity {$i}\");\n $this->info(\"Matching contact {$i}\");\n// }\n// $crmService->syncOpportunity('374720564');\n $crmService->matchByName('Robot');\n }\n }\n\n private function simulateMatchActivityStorm(int $teamId = 2, int $count = 100): void\n {\n $team = Team::find($teamId);\n $config = $team->getCrmConfiguration();\n\n $activities = Activity::query()\n ->where('crm_configuration_id', $config->getId())\n ->orderByDesc('id')\n ->limit($count)\n ->get();\n\n $this->info(\"Dispatching {$activities->count()} MatchActivityCrmData jobs (portal={$config->getId()})\");\n\n foreach ($activities as $activity) {\n MatchActivityCrmData::dispatch($activity->getId(), $config, true);\n }\n\n $this->info('Done. Watch logs and run jiminny:debug observeRateLimit to inspect cache state.');\n }\n\n private function simulateVerifyTaskStorm(int $teamId = 2, int $count = 100): void\n {\n $activities = Activity::query()\n ->where('team_id', $teamId)\n ->whereNotNull('crm_provider_id')\n ->orderByDesc('id')\n ->limit($count)\n ->get();\n\n $this->info(\"Dispatching {$activities->count()} VerifyActivityCrmTaskJob jobs\");\n\n foreach ($activities as $activity) {\n VerifyActivityCrmTaskJob::dispatch($activity->getId());\n }\n\n $this->info('Done.');\n }\n\n private function observeRateLimitCache(int $teamId = 2): void\n {\n $team = Team::find($teamId);\n $config = $team->getCrmConfiguration();\n $key = sprintf('hubspot:ratelimit:portal:%d', $config->getId());\n\n $value = Redis::get($key);\n $ttl = Redis::ttl($key);\n\n $this->info(\"Redis key: {$key}\");\n $this->info('Value: ' . ($value ?? '(empty)'));\n $this->info(\"TTL: {$ttl}s\");\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Console\\Commands;\n\nuse Carbon\\Carbon;\nuse Carbon\\CarbonImmutable;\nuse Illuminate\\Console\\Command;\nuse Illuminate\\Support\\Facades\\Redis;\nuse InvalidArgumentException;\nuse Jiminny\\Jobs\\AutomatedReports\\RequestGenerateAskJiminnyReportJob;\nuse Jiminny\\Jobs\\AutomatedReports\\SendReportMailJob;\nuse Jiminny\\Jobs\\Crm\\Delete\\VerifyActivityCrmTaskJob;\nuse Jiminny\\Jobs\\Crm\\MatchActivityCrmData;\nuse Jiminny\\Jobs\\JobDispatcherInterface;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\AutomatedReportResult;\nuse Jiminny\\Models\\Team;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Repositories\\AutomatedReportsRepository;\nuse Jiminny\\Services\\Activity\\CrmOwnerResolver;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\n\n/**\n * Class JiminnyDebugCommand\n *\n * @package Jiminny\\Console\\Commands\n */\nclass JiminnyDebugCommand extends Command\n{\n public const string FREQUENCY_DAILY = 'daily';\n public const string FREQUENCY_WEEKLY = 'weekly';\n public const string FREQUENCY_MONTHLY = 'monthly';\n public const string FREQUENCY_QUARTERLY = 'quarterly';\n public const string FREQUENCY_ONE_OFF = 'one_off';\n protected $signature = 'jiminny:debug';\n\n public function handle(\n JobDispatcherInterface $jobDispatcher,\n AutomatedReportsService $automatedReportsService,\n AutomatedReportsRepository $automatedReportsRepository,\n UserPilotClient $userPilotClient\n ): void {\n // Choose ONE of the following to run, then comment out the others.\n // 1) Dispatch a storm of MatchActivityCrmData jobs against team 2\n $this->simulateMatchActivityStorm(teamId: 2, count: 100);\n\n // 2) Dispatch a storm of VerifyActivityCrmTaskJob jobs (simulates DeleteCrmEntityTrait fan-out)\n // $this->simulateVerifyTaskStorm(teamId: 2, count: 100);\n\n // 3) Inspect Redis circuit-breaker state for the team's HubSpot portal\n // $this->observeRateLimitCache(teamId: 2);\n\n // 4) Make 3 synchronous matchByName calls (foreground, hits API directly)\n // $this->rateLimit();\n exit(1);\n\n\n\n $report = AutomatedReport::find(71);\n $last = AutomatedReportResult::query()\n ->where('report_id', $report->getId())\n ->whereIn('status', [AutomatedReportResult::STATUS_DEFAULT, AutomatedReportResult::STATUS_FAILED])\n// ->where('reason', '!=', AutomatedReportResult::REASON_NOT_ENOUGH_ACTIVITIES)\n ->whereDate('created_at', CarbonImmutable::now()->toDateString())\n ->latest()\n ->first();\n\n $this->info(\"Last: {$last->getId()}\");\n\n exit(1);\n\n $user = User::find(143);\n // $count = $automatedReportsRepository->countUserReports($user);\n // $this->info(\"Count: {$count}\");\n // $count = $automatedReportsRepository->countAllUserReports($user);\n // $this->info(\"All count: {$count}\");\n\n $payload = [\n 'report_type' => 'ask_jiminny',\n 'frequency' => 'weekly',\n ];\n $userPilotClient->track($user, 'ask-jiminny-report-generated', $payload);\n\n exit(1);\n\n $now = Carbon::now()->subDay(1);\n $this->info(\"Now: {$now->toDateTimeString()}\");\n $weekStart = Carbon::getWeekStartsAt();\n $this->info(\"Now: {$weekStart}\");\n\n // $from = $now->copy()->previousWeekday()->startOfDay();\n // $to = $now->copy()->previousWeekday()->endOfDay();\n\n // $fromOld = $now->copy()->subWeeks(1)->startOfDay();\n // $toOld = $now->copy()->subDay()->endOfDay();\n // $fromNew = $now->copy()->subWeek()->startOfWeek();\n // $toNew = $now->copy()->subWeek()->endOfWeek();\n\n // $fromOld = $now->copy()->subMonths(1)->startOfDay();\n // $toOld = $now->copy()->subDay()->endOfDay();\n // $fromNew = $now->copy()->subMonthNoOverflow()->startOfMonth();\n // $toNew = $now->copy()->subMonthNoOverflow()->endOfMonth();\n\n $fromOld = $now->copy()->subMonths(3)->startOfDay();\n $toOld = $now->copy()->subDay()->endOfDay();\n $fromNew = $now->copy()->subQuarterNoOverflow()->startOfQuarter();\n $toNew = $now->copy()->subQuarterNoOverflow()->endOfQuarter();\n\n $this->info(\"From old: {$fromOld->toDateTimeString()}\");\n $this->info(\"To old: {$toOld->toDateTimeString()}\");\n $this->info(\"From new: {$fromNew->toDateTimeString()}\");\n $this->info(\"To new: {$toNew->toDateTimeString()}\");\n\n exit(1);\n\n $report = AutomatedReport::find(71);\n\n $job = new RequestGenerateAskJiminnyReportJob($report->getUuid());\n $jobDispatcher->dispatch($job);\n\n exit(1);\n\n\n // $this->formatDate($jobDispatcher);\n // $this->sendMail($jobDispatcher, $automatedReportsService);\n // $this->crmService();\n\n $this->getPayload($automatedReportsService);\n\n exit(1);\n }\n\n\n\n private function crmService()\n {\n $activity = Activity::find(418141);\n\n $team = Team::find(19);\n $config = $team->getCrmConfiguration();\n\n $crmResolver = app(CrmOwnerResolver::class, [\n 'team' => $team,\n 'integrationAdmin' => $team->getOwner(),\n 'providerSlug' => $config->getProviderName(),\n ]);\n\n $crmService = $crmResolver->prepareCrmService();\n\n $crmService->createTranscriptNotes($activity);\n }\n\n private function sendMail(JobDispatcherInterface $jobDispatcher, AutomatedReportsService $automatedReportsService)\n {\n $reportUuid = '';\n // $report = $automatedReportsService->getReportResult($reportUuid);\n $report = AutomatedReportResult::find(275);\n $validRecipients = $automatedReportsService->getValidRecipientUsers(\n $report->getReport(),\n includeJiminny: true,\n );\n\n $recipient = $validRecipients[0];\n\n $fileName = $automatedReportsService->getReportFileName($report);\n $typeName = $report->getReport()->getCustomName()\n ?? $automatedReportsService->getReportTypeName($report);\n $teamsName = $automatedReportsService->getReportTeamsName($report);\n $periodName = $automatedReportsService->getReportPeriodName($report);\n $s3Path = $automatedReportsService->getMediaPath($report);\n\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$fileName ' . PHP_EOL . print_r($fileName, true));\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$typeName ' . PHP_EOL . print_r($typeName, true));\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$teamsName ' . PHP_EOL . print_r($teamsName, true));\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$periodName ' . PHP_EOL . print_r($periodName, true));\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$s3Path ' . PHP_EOL . print_r($s3Path, true));\n\n $jobDispatcher->dispatch(\n new SendReportMailJob(\n reportUuid: $report->getUuid(),\n s3Path: $s3Path,\n recipientEmail: $recipient['email'],\n recipientName: $recipient['name'] ?? null,\n fileName: $fileName,\n typeName: $typeName,\n teamsName: $teamsName,\n periodName: $periodName,\n isAskJiminny: true,\n )\n );\n\n exit(1);\n }\n\n private function formatDate(JobDispatcherInterface $jobDispatcher): void\n {\n $customName = 'Custom report name';\n // $frequency = self::FREQUENCY_DAILY;\n // $frequency = self::FREQUENCY_WEEKLY;\n $frequency = self::FREQUENCY_MONTHLY;\n // $frequency = self::FREQUENCY_QUARTERLY;\n // $frequency = self::FREQUENCY_ONE_OFF;\n $period = $this->calculateFromAndToDatePeriod($frequency);\n $from = $period['fromDate'];\n $to = $period['toDate'];\n $periodName = $this->formatReportPeriodName($frequency, $from, $to);\n $filenameSuffix = null;\n\n if ($customName) {\n if ($filenameSuffix) {\n $customName .= \" {$filenameSuffix}\";\n }\n\n $result = $this->sanitizeFileName(\"{$customName} - {$periodName}\");\n }\n\n $this->info($result);\n }\n\n public function calculateFromAndToDatePeriod(\n string $frequency,\n ?Carbon $fromDate = null,\n ?Carbon $toDate = null\n ): array {\n if ($frequency === self::FREQUENCY_ONE_OFF) {\n return [\n 'fromDate' => $fromDate,\n 'toDate' => $toDate,\n ];\n }\n\n $now = Carbon::now();\n\n return match ($frequency) {\n self::FREQUENCY_DAILY => [\n 'fromDate' => $now->copy()->subDay()->startOfDay(),\n 'toDate' => $now->copy()->subDay()->endOfDay(),\n ],\n self::FREQUENCY_WEEKLY => [\n 'fromDate' => $now->copy()->subWeeks(1)->startOfDay(),\n 'toDate' => $now->copy()->subDay()->endOfDay(),\n ],\n self::FREQUENCY_MONTHLY => [\n 'fromDate' => $now->copy()->subMonths(1)->startOfDay(),\n 'toDate' => $now->copy()->subDay()->endOfDay(),\n ],\n self::FREQUENCY_QUARTERLY => [\n 'fromDate' => $now->copy()->subMonths(3)->startOfDay(),\n 'toDate' => $now->copy()->subDay()->endOfDay(),\n ],\n default => throw new InvalidArgumentException(\"Unsupported frequency: {$frequency}\"),\n };\n }\n\n private function formatReportPeriodName(string $frequency, Carbon $from, Carbon $to): string\n {\n $fromYear = $from->format('Y');\n $toYear = $to->format('Y');\n $differentYears = $fromYear !== $toYear;\n\n switch ($frequency) {\n case self::FREQUENCY_DAILY:\n return $from->format('j M Y');\n\n case self::FREQUENCY_QUARTERLY:\n // 'Jan-Mar 2025' or 'Nov 2024-Jan 2025' if years differ\n $startMonth = $from->format('M');\n $endMonth = $to->copy()->subMonth();\n $endMonthName = $endMonth->format('M');\n $endMonthYear = $endMonth->format('Y');\n\n if ($differentYears) {\n return \"{$startMonth} {$fromYear} - {$endMonthName} {$endMonthYear}\";\n }\n\n return \"{$startMonth} - {$endMonthName} {$toYear}\";\n\n case self::FREQUENCY_MONTHLY:\n // 'May 2025' - monthly reports are always within the same year\n return $from->format('M Y');\n\n case self::FREQUENCY_WEEKLY:\n // '4 - 8 Aug 2025', '27 Oct - 3 Nov 2025', or '28 Dec 2024 - 3 Jan 2025' if years differ\n $startDay = $from->format('j');\n $endDay = $to->format('j');\n $startMonth = $from->format('M');\n $endMonth = $to->format('M');\n\n if ($differentYears) {\n return \"{$startDay} {$startMonth} {$fromYear} - {$endDay} {$endMonth} {$toYear}\";\n }\n\n if ($startMonth !== $endMonth) {\n return \"{$startDay} {$startMonth} - {$endDay} {$endMonth} {$toYear}\";\n }\n\n return \"{$startDay} - {$endDay} {$endMonth} {$toYear}\";\n\n case self::FREQUENCY_ONE_OFF:\n // '2 May-31 May 2025' or '15 Dec 2024-15 Jan 2025' if years differ\n $startDay = $from->format('j');\n $startMonth = $from->format('M');\n $endDay = $to->format('j');\n $endMonth = $to->format('M');\n\n // If same month and year, use a format like '2-31 May 2025'\n if ($startMonth === $endMonth && ! $differentYears) {\n return \"{$startDay} - {$endDay} {$startMonth} {$toYear}\";\n }\n\n // If different years, include both years\n if ($differentYears) {\n return \"{$startDay} {$startMonth} {$fromYear} - {$endDay} {$endMonth} {$toYear}\";\n }\n\n // Same year but different months\n return \"{$startDay} {$startMonth} - {$endDay} {$endMonth} {$toYear}\";\n\n default:\n // Default format for unknown frequencies\n return $from->format('j M Y') . ' - ' . $to->format('j M Y');\n }\n }\n\n public function sanitizeFileName(string $fileName): string\n {\n return str_replace(['/', '\\\\'], '-', $fileName);\n }\n\n private function getPayload(AutomatedReportsService $automatedReportsService)\n {\n $reportResult = AutomatedReportResult::find(269);\n $automatedReport = $reportResult->getReport();\n $activityIds = [1,2,3];\n $payload = $automatedReportsService->getAskJiminnyGenerateReportPayload(\n automatedReport: $automatedReport,\n reportResult: $reportResult,\n activityIds: $activityIds,\n );\n\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$payload ' . PHP_EOL . print_r($payload, true));\n }\n\n private function rateLimit()\n {\n $team = Team::find(2);\n $config = $team->getCrmConfiguration();\n\n $crmResolver = app(CrmOwnerResolver::class, [\n 'team' => $team,\n 'integrationAdmin' => $team->getOwner(),\n 'providerSlug' => $config->getProviderName(),\n ]);\n\n $crmService = $crmResolver->prepareCrmService();\n\n for ($i = 0 ; $i < 3; $i++) {\n// if ($i % 25 === 0) {\n// $this->info(\"Syncing opportunity {$i}\");\n $this->info(\"Matching contact {$i}\");\n// }\n// $crmService->syncOpportunity('374720564');\n $crmService->matchByName('Robot');\n }\n }\n\n private function simulateMatchActivityStorm(int $teamId = 2, int $count = 100): void\n {\n $team = Team::find($teamId);\n $config = $team->getCrmConfiguration();\n\n $activities = Activity::query()\n ->where('crm_configuration_id', $config->getId())\n ->orderByDesc('id')\n ->limit($count)\n ->get();\n\n $this->info(\"Dispatching {$activities->count()} MatchActivityCrmData jobs (portal={$config->getId()})\");\n\n foreach ($activities as $activity) {\n MatchActivityCrmData::dispatch($activity->getId(), $config, true);\n }\n\n $this->info('Done. Watch logs and run jiminny:debug observeRateLimit to inspect cache state.');\n }\n\n private function simulateVerifyTaskStorm(int $teamId = 2, int $count = 100): void\n {\n $activities = Activity::query()\n ->where('team_id', $teamId)\n ->whereNotNull('crm_provider_id')\n ->orderByDesc('id')\n ->limit($count)\n ->get();\n\n $this->info(\"Dispatching {$activities->count()} VerifyActivityCrmTaskJob jobs\");\n\n foreach ($activities as $activity) {\n VerifyActivityCrmTaskJob::dispatch($activity->getId());\n }\n\n $this->info('Done.');\n }\n\n private function observeRateLimitCache(int $teamId = 2): void\n {\n $team = Team::find($teamId);\n $config = $team->getCrmConfiguration();\n $key = sprintf('hubspot:ratelimit:portal:%d', $config->getId());\n\n $value = Redis::get($key);\n $ttl = Redis::ttl($key);\n\n $this->info(\"Redis key: {$key}\");\n $this->info('Value: ' . ($value ?? '(empty)'));\n $this->info(\"TTL: {$ttl}s\");\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Show Replace Field","depth":4,"bounds":{"left":0.5575133,"top":0.08060654,"width":0.008643617,"height":0.01915403},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Search History","depth":3,"bounds":{"left":0.57014626,"top":0.07980846,"width":0.00731383,"height":0.017557861},"on_screen":true,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"429","depth":4,"bounds":{"left":0.58111703,"top":0.07980846,"width":0.077792555,"height":0.015961692},"on_screen":true,"value":"429","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"New Line","depth":3,"bounds":{"left":0.66788566,"top":0.07980846,"width":0.00731383,"height":0.017557861},"on_screen":true,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Match Case","depth":3,"bounds":{"left":0.67785907,"top":0.07980846,"width":0.00731383,"height":0.017557861},"on_screen":true,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Words","depth":3,"bounds":{"left":0.68650264,"top":0.07980846,"width":0.00731383,"height":0.017557861},"on_screen":true,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Regex","depth":3,"bounds":{"left":0.69514626,"top":0.07980846,"width":0.00731383,"height":0.017557861},"on_screen":true,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Replace History","depth":3,"bounds":{"left":0.27027926,"top":1.0,"width":0.00731383,"height":0.0},"on_screen":false,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"Replace","depth":4,"on_screen":false,"role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"New Line","depth":3,"bounds":{"left":0.27027926,"top":1.0,"width":0.00731383,"height":0.0},"on_screen":false,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Preserve case","depth":3,"bounds":{"left":0.27027926,"top":1.0,"width":0.00731383,"height":0.0},"on_screen":false,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
8469261082636067262
|
3603241459212429739
|
click
|
accessibility
|
NULL
|
Project: faVsco.js, menu
JY-20725-handle-HS-search Project: faVsco.js, menu
JY-20725-handle-HS-search-rate-limit, menu
Start Listening for PHP Debug Connections
HandleHubspotRateLimitTest
Run 'HandleHubspotRateLimitTest'
Debug 'HandleHubspotRateLimitTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
5
133
11
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Console\Commands;
use Carbon\Carbon;
use Carbon\CarbonImmutable;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Redis;
use InvalidArgumentException;
use Jiminny\Jobs\AutomatedReports\RequestGenerateAskJiminnyReportJob;
use Jiminny\Jobs\AutomatedReports\SendReportMailJob;
use Jiminny\Jobs\Crm\Delete\VerifyActivityCrmTaskJob;
use Jiminny\Jobs\Crm\MatchActivityCrmData;
use Jiminny\Jobs\JobDispatcherInterface;
use Jiminny\Models\Activity;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\AutomatedReportResult;
use Jiminny\Models\Team;
use Jiminny\Models\User;
use Jiminny\Repositories\AutomatedReportsRepository;
use Jiminny\Services\Activity\CrmOwnerResolver;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
/**
* Class JiminnyDebugCommand
*
* @package Jiminny\Console\Commands
*/
class JiminnyDebugCommand extends Command
{
public const string FREQUENCY_DAILY = 'daily';
public const string FREQUENCY_WEEKLY = 'weekly';
public const string FREQUENCY_MONTHLY = 'monthly';
public const string FREQUENCY_QUARTERLY = 'quarterly';
public const string FREQUENCY_ONE_OFF = 'one_off';
protected $signature = 'jiminny:debug';
public function handle(
JobDispatcherInterface $jobDispatcher,
AutomatedReportsService $automatedReportsService,
AutomatedReportsRepository $automatedReportsRepository,
UserPilotClient $userPilotClient
): void {
// Choose ONE of the following to run, then comment out the others.
// 1) Dispatch a storm of MatchActivityCrmData jobs against team 2
$this->simulateMatchActivityStorm(teamId: 2, count: 100);
// 2) Dispatch a storm of VerifyActivityCrmTaskJob jobs (simulates DeleteCrmEntityTrait fan-out)
// $this->simulateVerifyTaskStorm(teamId: 2, count: 100);
// 3) Inspect Redis circuit-breaker state for the team's HubSpot portal
// $this->observeRateLimitCache(teamId: 2);
// 4) Make 3 synchronous matchByName calls (foreground, hits API directly)
// $this->rateLimit();
exit(1);
$report = AutomatedReport::find(71);
$last = AutomatedReportResult::query()
->where('report_id', $report->getId())
->whereIn('status', [AutomatedReportResult::STATUS_DEFAULT, AutomatedReportResult::STATUS_FAILED])
// ->where('reason', '!=', AutomatedReportResult::REASON_NOT_ENOUGH_ACTIVITIES)
->whereDate('created_at', CarbonImmutable::now()->toDateString())
->latest()
->first();
$this->info("Last: {$last->getId()}");
exit(1);
$user = User::find(143);
// $count = $automatedReportsRepository->countUserReports($user);
// $this->info("Count: {$count}");
// $count = $automatedReportsRepository->countAllUserReports($user);
// $this->info("All count: {$count}");
$payload = [
'report_type' => 'ask_jiminny',
'frequency' => 'weekly',
];
$userPilotClient->track($user, 'ask-jiminny-report-generated', $payload);
exit(1);
$now = Carbon::now()->subDay(1);
$this->info("Now: {$now->toDateTimeString()}");
$weekStart = Carbon::getWeekStartsAt();
$this->info("Now: {$weekStart}");
// $from = $now->copy()->previousWeekday()->startOfDay();
// $to = $now->copy()->previousWeekday()->endOfDay();
// $fromOld = $now->copy()->subWeeks(1)->startOfDay();
// $toOld = $now->copy()->subDay()->endOfDay();
// $fromNew = $now->copy()->subWeek()->startOfWeek();
// $toNew = $now->copy()->subWeek()->endOfWeek();
// $fromOld = $now->copy()->subMonths(1)->startOfDay();
// $toOld = $now->copy()->subDay()->endOfDay();
// $fromNew = $now->copy()->subMonthNoOverflow()->startOfMonth();
// $toNew = $now->copy()->subMonthNoOverflow()->endOfMonth();
$fromOld = $now->copy()->subMonths(3)->startOfDay();
$toOld = $now->copy()->subDay()->endOfDay();
$fromNew = $now->copy()->subQuarterNoOverflow()->startOfQuarter();
$toNew = $now->copy()->subQuarterNoOverflow()->endOfQuarter();
$this->info("From old: {$fromOld->toDateTimeString()}");
$this->info("To old: {$toOld->toDateTimeString()}");
$this->info("From new: {$fromNew->toDateTimeString()}");
$this->info("To new: {$toNew->toDateTimeString()}");
exit(1);
$report = AutomatedReport::find(71);
$job = new RequestGenerateAskJiminnyReportJob($report->getUuid());
$jobDispatcher->dispatch($job);
exit(1);
// $this->formatDate($jobDispatcher);
// $this->sendMail($jobDispatcher, $automatedReportsService);
// $this->crmService();
$this->getPayload($automatedReportsService);
exit(1);
}
private function crmService()
{
$activity = Activity::find(418141);
$team = Team::find(19);
$config = $team->getCrmConfiguration();
$crmResolver = app(CrmOwnerResolver::class, [
'team' => $team,
'integrationAdmin' => $team->getOwner(),
'providerSlug' => $config->getProviderName(),
]);
$crmService = $crmResolver->prepareCrmService();
$crmService->createTranscriptNotes($activity);
}
private function sendMail(JobDispatcherInterface $jobDispatcher, AutomatedReportsService $automatedReportsService)
{
$reportUuid = '';
// $report = $automatedReportsService->getReportResult($reportUuid);
$report = AutomatedReportResult::find(275);
$validRecipients = $automatedReportsService->getValidRecipientUsers(
$report->getReport(),
includeJiminny: true,
);
$recipient = $validRecipients[0];
$fileName = $automatedReportsService->getReportFileName($report);
$typeName = $report->getReport()->getCustomName()
?? $automatedReportsService->getReportTypeName($report);
$teamsName = $automatedReportsService->getReportTeamsName($report);
$periodName = $automatedReportsService->getReportPeriodName($report);
$s3Path = $automatedReportsService->getMediaPath($report);
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$fileName ' . PHP_EOL . print_r($fileName, true));
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$typeName ' . PHP_EOL . print_r($typeName, true));
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$teamsName ' . PHP_EOL . print_r($teamsName, true));
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$periodName ' . PHP_EOL . print_r($periodName, true));
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$s3Path ' . PHP_EOL . print_r($s3Path, true));
$jobDispatcher->dispatch(
new SendReportMailJob(
reportUuid: $report->getUuid(),
s3Path: $s3Path,
recipientEmail: $recipient['email'],
recipientName: $recipient['name'] ?? null,
fileName: $fileName,
typeName: $typeName,
teamsName: $teamsName,
periodName: $periodName,
isAskJiminny: true,
)
);
exit(1);
}
private function formatDate(JobDispatcherInterface $jobDispatcher): void
{
$customName = 'Custom report name';
// $frequency = self::FREQUENCY_DAILY;
// $frequency = self::FREQUENCY_WEEKLY;
$frequency = self::FREQUENCY_MONTHLY;
// $frequency = self::FREQUENCY_QUARTERLY;
// $frequency = self::FREQUENCY_ONE_OFF;
$period = $this->calculateFromAndToDatePeriod($frequency);
$from = $period['fromDate'];
$to = $period['toDate'];
$periodName = $this->formatReportPeriodName($frequency, $from, $to);
$filenameSuffix = null;
if ($customName) {
if ($filenameSuffix) {
$customName .= " {$filenameSuffix}";
}
$result = $this->sanitizeFileName("{$customName} - {$periodName}");
}
$this->info($result);
}
public function calculateFromAndToDatePeriod(
string $frequency,
?Carbon $fromDate = null,
?Carbon $toDate = null
): array {
if ($frequency === self::FREQUENCY_ONE_OFF) {
return [
'fromDate' => $fromDate,
'toDate' => $toDate,
];
}
$now = Carbon::now();
return match ($frequency) {
self::FREQUENCY_DAILY => [
'fromDate' => $now->copy()->subDay()->startOfDay(),
'toDate' => $now->copy()->subDay()->endOfDay(),
],
self::FREQUENCY_WEEKLY => [
'fromDate' => $now->copy()->subWeeks(1)->startOfDay(),
'toDate' => $now->copy()->subDay()->endOfDay(),
],
self::FREQUENCY_MONTHLY => [
'fromDate' => $now->copy()->subMonths(1)->startOfDay(),
'toDate' => $now->copy()->subDay()->endOfDay(),
],
self::FREQUENCY_QUARTERLY => [
'fromDate' => $now->copy()->subMonths(3)->startOfDay(),
'toDate' => $now->copy()->subDay()->endOfDay(),
],
default => throw new InvalidArgumentException("Unsupported frequency: {$frequency}"),
};
}
private function formatReportPeriodName(string $frequency, Carbon $from, Carbon $to): string
{
$fromYear = $from->format('Y');
$toYear = $to->format('Y');
$differentYears = $fromYear !== $toYear;
switch ($frequency) {
case self::FREQUENCY_DAILY:
return $from->format('j M Y');
case self::FREQUENCY_QUARTERLY:
// 'Jan-Mar 2025' or 'Nov 2024-Jan 2025' if years differ
$startMonth = $from->format('M');
$endMonth = $to->copy()->subMonth();
$endMonthName = $endMonth->format('M');
$endMonthYear = $endMonth->format('Y');
if ($differentYears) {
return "{$startMonth} {$fromYear} - {$endMonthName} {$endMonthYear}";
}
return "{$startMonth} - {$endMonthName} {$toYear}";
case self::FREQUENCY_MONTHLY:
// 'May 2025' - monthly reports are always within the same year
return $from->format('M Y');
case self::FREQUENCY_WEEKLY:
// '4 - 8 Aug 2025', '27 Oct - 3 Nov 2025', or '28 Dec 2024 - 3 Jan 2025' if years differ
$startDay = $from->format('j');
$endDay = $to->format('j');
$startMonth = $from->format('M');
$endMonth = $to->format('M');
if ($differentYears) {
return "{$startDay} {$startMonth} {$fromYear} - {$endDay} {$endMonth} {$toYear}";
}
if ($startMonth !== $endMonth) {
return "{$startDay} {$startMonth} - {$endDay} {$endMonth} {$toYear}";
}
return "{$startDay} - {$endDay} {$endMonth} {$toYear}";
case self::FREQUENCY_ONE_OFF:
// '2 May-31 May 2025' or '15 Dec 2024-15 Jan 2025' if years differ
$startDay = $from->format('j');
$startMonth = $from->format('M');
$endDay = $to->format('j');
$endMonth = $to->format('M');
// If same month and year, use a format like '2-31 May 2025'
if ($startMonth === $endMonth && ! $differentYears) {
return "{$startDay} - {$endDay} {$startMonth} {$toYear}";
}
// If different years, include both years
if ($differentYears) {
return "{$startDay} {$startMonth} {$fromYear} - {$endDay} {$endMonth} {$toYear}";
}
// Same year but different months
return "{$startDay} {$startMonth} - {$endDay} {$endMonth} {$toYear}";
default:
// Default format for unknown frequencies
return $from->format('j M Y') . ' - ' . $to->format('j M Y');
}
}
public function sanitizeFileName(string $fileName): string
{
return str_replace(['/', '\\'], '-', $fileName);
}
private function getPayload(AutomatedReportsService $automatedReportsService)
{
$reportResult = AutomatedReportResult::find(269);
$automatedReport = $reportResult->getReport();
$activityIds = [1,2,3];
$payload = $automatedReportsService->getAskJiminnyGenerateReportPayload(
automatedReport: $automatedReport,
reportResult: $reportResult,
activityIds: $activityIds,
);
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$payload ' . PHP_EOL . print_r($payload, true));
}
private function rateLimit()
{
$team = Team::find(2);
$config = $team->getCrmConfiguration();
$crmResolver = app(CrmOwnerResolver::class, [
'team' => $team,
'integrationAdmin' => $team->getOwner(),
'providerSlug' => $config->getProviderName(),
]);
$crmService = $crmResolver->prepareCrmService();
for ($i = 0 ; $i < 3; $i++) {
// if ($i % 25 === 0) {
// $this->info("Syncing opportunity {$i}");
$this->info("Matching contact {$i}");
// }
// $crmService->syncOpportunity('374720564');
$crmService->matchByName('Robot');
}
}
private function simulateMatchActivityStorm(int $teamId = 2, int $count = 100): void
{
$team = Team::find($teamId);
$config = $team->getCrmConfiguration();
$activities = Activity::query()
->where('crm_configuration_id', $config->getId())
->orderByDesc('id')
->limit($count)
->get();
$this->info("Dispatching {$activities->count()} MatchActivityCrmData jobs (portal={$config->getId()})");
foreach ($activities as $activity) {
MatchActivityCrmData::dispatch($activity->getId(), $config, true);
}
$this->info('Done. Watch logs and run jiminny:debug observeRateLimit to inspect cache state.');
}
private function simulateVerifyTaskStorm(int $teamId = 2, int $count = 100): void
{
$activities = Activity::query()
->where('team_id', $teamId)
->whereNotNull('crm_provider_id')
->orderByDesc('id')
->limit($count)
->get();
$this->info("Dispatching {$activities->count()} VerifyActivityCrmTaskJob jobs");
foreach ($activities as $activity) {
VerifyActivityCrmTaskJob::dispatch($activity->getId());
}
$this->info('Done.');
}
private function observeRateLimitCache(int $teamId = 2): void
{
$team = Team::find($teamId);
$config = $team->getCrmConfiguration();
$key = sprintf('hubspot:ratelimit:portal:%d', $config->getId());
$value = Redis::get($key);
$ttl = Redis::ttl($key);
$this->info("Redis key: {$key}");
$this->info('Value: ' . ($value ?? '(empty)'));
$this->info("TTL: {$ttl}s");
}
}
Show Replace Field
Search History
429
New Line
Match Case
Words
Regex
Replace History
Replace
New Line
Preserve case...
|
18246
|
NULL
|
NULL
|
NULL
|
|
18248
|
790
|
29
|
2026-05-11T11:06:53.262130+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-11/1778 /Users/lukas/.screenpipe/data/data/2026-05-11/1778497613262_m1.jpg...
|
PhpStorm
|
faVsco.js – laravel.log
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
JY-20725-handle-HS-search Project: faVsco.js, menu
JY-20725-handle-HS-search-rate-limit, menu
Start Listening for PHP Debug Connections
HandleHubspotRateLimitTest
Run 'HandleHubspotRateLimitTest'
Debug 'HandleHubspotRateLimitTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JY-20725-handle-HS-search-rate-limit, menu","depth":5,"on_screen":true,"help_text":"Git Branch: JY-20725-handle-HS-search-rate-limit","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"HandleHubspotRateLimitTest","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'HandleHubspotRateLimitTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'HandleHubspotRateLimitTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
1579713784452094674
|
-8780890023316608054
|
click
|
hybrid
|
NULL
|
Project: faVsco.js, menu
JY-20725-handle-HS-search Project: faVsco.js, menu
JY-20725-handle-HS-search-rate-limit, menu
Start Listening for PHP Debug Connections
HandleHubspotRateLimitTest
Run 'HandleHubspotRateLimitTest'
Debug 'HandleHubspotRateLimitTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
SlackFileEditViewGoHistoryWindowHelp• 0DOCKERcompiledeventsroutesviewsO ₴1DEV (docker)$2APP (-zsh)883Jiminny-worker-processing-delayed: jiminny-worker-processing-delayed_00:stoppedjiminny-worker-processing-2:jiminny-worker-processing-2_00: stoppedjiminny-worker-processing-3:jiminny-worker-processing-3_00: stoppedjiminny-worker-processing-4:jiminny-worker-processing-4_00:stoppedjiminny-worker-processing-5: jiminny-worker-processing-5_00:stoppedworker-analytics:worker-analytics_00: stoppedworker-crm-update:worker-crm-update_00: stoppedworker-download:worker-download_00:stoppedworker-nudges:worker-nudges_00:stoppedjiminny-worker-processing-1:jiminny-worker-processing-1_00: stoppedworker:worker_00: stoppedworker-calendar:worker-calendar_00:worker-conferences:worker-conferences_00:worker-crm-sync:worker-crm-sync_00: stoppedworker-es-update:worker-es-update_00:worker-emails:worker-emails_00: stoppedworker-audio:worker-audio_00: stoppedartisan-schedule:artisan-schedule_00: stoppedartisan-schedule:artisan-schedule_00: startedjiminny-worker-processing-1:jiminny-worker-processing-1_00: startedjiminny-worker-processing-2:jiminny-worker-processing-2_00: startedjiminny-worker-processing-3:jiminny-worker-processing-3_00: startedjiminny-worker-processing-4:jiminny-worker-processing-4_00: startedjiminny-worker-processing-5:jiminny-worker-processing-5_00:startedjiminny-worker-processing-delayed: jiminny-worker-processing-delayed_00: startedworker:worker_00: startedworker-analytics:worker-analytics_00: startedworker-audio:worker-audio_00: startedworker-calendar:worker-calendar_00:startedworker-conferences:worker-conferences_00: startedworker-crm-sync:worker-crm-sync_00: startedworker-crm-update:worker-crm-update_00: startedworker-download:worker-download_00: startedworker-emails:worker-emails_00: startedworker-es-update:worker-es-update_00:startedworker-nudges:worker-nudges_00: startedroot@docker_lamp_1:/home/jiminny#php artisan jiminny:debugDispatching 100 MatchActivityCrmDatajobs (portal=2)Done.Watch logs and runjiminny:debug observeRateLimit to inspect cache state.root@docker_lamp_1:/home/jiminny# ]DEV (dl-zshHomeDMsActivityFilesLater...Morealo)• Preparation for Refi... in 54 m100% <78• Mon 11 May 14:06:52→Describe what you are looking forJiminny ...# contusion-clinic# curiosity_lab# engineering# general# jiminny-bg# platform-tickets# product_launches# random# releases# sofia-office# support# thank-yous# the_people_of jimi...^ Direct messagesP. Aneliya Angelova®. Galya Dimitrova& Petko Kashinski&. Stefka StoyanovaVasil Vasilev&. Nikolay IvanovAneliya Angelova, ...Stoyan Tanev. VesE Lukas Kovalik y…..::: AppsS Jira CloudToastGanala Cala# thank-yous406 0Messagest* Add canvas+O FilesWednesday, May 6th~It shows increaiuie skir tu jump into Supportand not miss a beat.More importantly, itshows great heart and helped delight ourcustomers on two ways at the same time:Support and Trials.You embodied so many of our values duringthis period.#selflessness #lowego #customerfocus#positiveattitudeView Recognition Moment10011 reply 5 days agoToday ~Greg 1:31 PMShout out to engineering/infrastructure teamand their stability work. Did you know ... last weekour Product had 100% uptime? ..not99.9999999% but /2 %!lots of little things (and a few big things) make thathappen. well done all!81#1NewKara Jones 1:54 PM100!!!!!! That is amazing!!! Power team!!Message #thank-yous+...
|
18245
|
NULL
|
NULL
|
NULL
|
|
18249
|
791
|
31
|
2026-05-11T11:06:54.066306+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-11/1778 /Users/lukas/.screenpipe/data/data/2026-05-11/1778497614066_m2.jpg...
|
PhpStorm
|
faVsco.js – laravel.log
|
1
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
JY-20725-handle-HS-search Project: faVsco.js, menu
JY-20725-handle-HS-search-rate-limit, menu
Start Listening for PHP Debug Connections
HandleHubspotRateLimitTest
Run 'HandleHubspotRateLimitTest'
Debug 'HandleHubspotRateLimitTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
5
133
11
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Console\Commands;
use Carbon\Carbon;
use Carbon\CarbonImmutable;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Redis;
use InvalidArgumentException;
use Jiminny\Jobs\AutomatedReports\RequestGenerateAskJiminnyReportJob;
use Jiminny\Jobs\AutomatedReports\SendReportMailJob;
use Jiminny\Jobs\Crm\Delete\VerifyActivityCrmTaskJob;
use Jiminny\Jobs\Crm\MatchActivityCrmData;
use Jiminny\Jobs\JobDispatcherInterface;
use Jiminny\Models\Activity;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\AutomatedReportResult;
use Jiminny\Models\Team;
use Jiminny\Models\User;
use Jiminny\Repositories\AutomatedReportsRepository;
use Jiminny\Services\Activity\CrmOwnerResolver;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
/**
* Class JiminnyDebugCommand
*
* @package Jiminny\Console\Commands
*/
class JiminnyDebugCommand extends Command
{
public const string FREQUENCY_DAILY = 'daily';
public const string FREQUENCY_WEEKLY = 'weekly';
public const string FREQUENCY_MONTHLY = 'monthly';
public const string FREQUENCY_QUARTERLY = 'quarterly';
public const string FREQUENCY_ONE_OFF = 'one_off';
protected $signature = 'jiminny:debug';
public function handle(
JobDispatcherInterface $jobDispatcher,
AutomatedReportsService $automatedReportsService,
AutomatedReportsRepository $automatedReportsRepository,
UserPilotClient $userPilotClient
): void {
// Choose ONE of the following to run, then comment out the others.
// 1) Dispatch a storm of MatchActivityCrmData jobs against team 2
$this->simulateMatchActivityStorm(teamId: 2, count: 100);
// 2) Dispatch a storm of VerifyActivityCrmTaskJob jobs (simulates DeleteCrmEntityTrait fan-out)
// $this->simulateVerifyTaskStorm(teamId: 2, count: 100);
// 3) Inspect Redis circuit-breaker state for the team's HubSpot portal
// $this->observeRateLimitCache(teamId: 2);
// 4) Make 3 synchronous matchByName calls (foreground, hits API directly)
// $this->rateLimit();
exit(1);
$report = AutomatedReport::find(71);
$last = AutomatedReportResult::query()
->where('report_id', $report->getId())
->whereIn('status', [AutomatedReportResult::STATUS_DEFAULT, AutomatedReportResult::STATUS_FAILED])
// ->where('reason', '!=', AutomatedReportResult::REASON_NOT_ENOUGH_ACTIVITIES)
->whereDate('created_at', CarbonImmutable::now()->toDateString())
->latest()
->first();
$this->info("Last: {$last->getId()}");
exit(1);
$user = User::find(143);
// $count = $automatedReportsRepository->countUserReports($user);
// $this->info("Count: {$count}");
// $count = $automatedReportsRepository->countAllUserReports($user);
// $this->info("All count: {$count}");
$payload = [
'report_type' => 'ask_jiminny',
'frequency' => 'weekly',
];
$userPilotClient->track($user, 'ask-jiminny-report-generated', $payload);
exit(1);
$now = Carbon::now()->subDay(1);
$this->info("Now: {$now->toDateTimeString()}");
$weekStart = Carbon::getWeekStartsAt();
$this->info("Now: {$weekStart}");
// $from = $now->copy()->previousWeekday()->startOfDay();
// $to = $now->copy()->previousWeekday()->endOfDay();
// $fromOld = $now->copy()->subWeeks(1)->startOfDay();
// $toOld = $now->copy()->subDay()->endOfDay();
// $fromNew = $now->copy()->subWeek()->startOfWeek();
// $toNew = $now->copy()->subWeek()->endOfWeek();
// $fromOld = $now->copy()->subMonths(1)->startOfDay();
// $toOld = $now->copy()->subDay()->endOfDay();
// $fromNew = $now->copy()->subMonthNoOverflow()->startOfMonth();
// $toNew = $now->copy()->subMonthNoOverflow()->endOfMonth();
$fromOld = $now->copy()->subMonths(3)->startOfDay();
$toOld = $now->copy()->subDay()->endOfDay();
$fromNew = $now->copy()->subQuarterNoOverflow()->startOfQuarter();
$toNew = $now->copy()->subQuarterNoOverflow()->endOfQuarter();
$this->info("From old: {$fromOld->toDateTimeString()}");
$this->info("To old: {$toOld->toDateTimeString()}");
$this->info("From new: {$fromNew->toDateTimeString()}");
$this->info("To new: {$toNew->toDateTimeString()}");
exit(1);
$report = AutomatedReport::find(71);
$job = new RequestGenerateAskJiminnyReportJob($report->getUuid());
$jobDispatcher->dispatch($job);
exit(1);
// $this->formatDate($jobDispatcher);
// $this->sendMail($jobDispatcher, $automatedReportsService);
// $this->crmService();
$this->getPayload($automatedReportsService);
exit(1);
}
private function crmService()
{
$activity = Activity::find(418141);
$team = Team::find(19);
$config = $team->getCrmConfiguration();
$crmResolver = app(CrmOwnerResolver::class, [
'team' => $team,
'integrationAdmin' => $team->getOwner(),
'providerSlug' => $config->getProviderName(),
]);
$crmService = $crmResolver->prepareCrmService();
$crmService->createTranscriptNotes($activity);
}
private function sendMail(JobDispatcherInterface $jobDispatcher, AutomatedReportsService $automatedReportsService)
{
$reportUuid = '';
// $report = $automatedReportsService->getReportResult($reportUuid);
$report = AutomatedReportResult::find(275);
$validRecipients = $automatedReportsService->getValidRecipientUsers(
$report->getReport(),
includeJiminny: true,
);
$recipient = $validRecipients[0];
$fileName = $automatedReportsService->getReportFileName($report);
$typeName = $report->getReport()->getCustomName()
?? $automatedReportsService->getReportTypeName($report);
$teamsName = $automatedReportsService->getReportTeamsName($report);
$periodName = $automatedReportsService->getReportPeriodName($report);
$s3Path = $automatedReportsService->getMediaPath($report);
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$fileName ' . PHP_EOL . print_r($fileName, true));
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$typeName ' . PHP_EOL . print_r($typeName, true));
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$teamsName ' . PHP_EOL . print_r($teamsName, true));
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$periodName ' . PHP_EOL . print_r($periodName, true));
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$s3Path ' . PHP_EOL . print_r($s3Path, true));
$jobDispatcher->dispatch(
new SendReportMailJob(
reportUuid: $report->getUuid(),
s3Path: $s3Path,
recipientEmail: $recipient['email'],
recipientName: $recipient['name'] ?? null,
fileName: $fileName,
typeName: $typeName,
teamsName: $teamsName,
periodName: $periodName,
isAskJiminny: true,
)
);
exit(1);
}
private function formatDate(JobDispatcherInterface $jobDispatcher): void
{
$customName = 'Custom report name';
// $frequency = self::FREQUENCY_DAILY;
// $frequency = self::FREQUENCY_WEEKLY;
$frequency = self::FREQUENCY_MONTHLY;
// $frequency = self::FREQUENCY_QUARTERLY;
// $frequency = self::FREQUENCY_ONE_OFF;
$period = $this->calculateFromAndToDatePeriod($frequency);
$from = $period['fromDate'];
$to = $period['toDate'];
$periodName = $this->formatReportPeriodName($frequency, $from, $to);
$filenameSuffix = null;
if ($customName) {
if ($filenameSuffix) {
$customName .= " {$filenameSuffix}";
}
$result = $this->sanitizeFileName("{$customName} - {$periodName}");
}
$this->info($result);
}
public function calculateFromAndToDatePeriod(
string $frequency,
?Carbon $fromDate = null,
?Carbon $toDate = null
): array {
if ($frequency === self::FREQUENCY_ONE_OFF) {
return [
'fromDate' => $fromDate,
'toDate' => $toDate,
];
}
$now = Carbon::now();
return match ($frequency) {
self::FREQUENCY_DAILY => [
'fromDate' => $now->copy()->subDay()->startOfDay(),
'toDate' => $now->copy()->subDay()->endOfDay(),
],
self::FREQUENCY_WEEKLY => [
'fromDate' => $now->copy()->subWeeks(1)->startOfDay(),
'toDate' => $now->copy()->subDay()->endOfDay(),
],
self::FREQUENCY_MONTHLY => [
'fromDate' => $now->copy()->subMonths(1)->startOfDay(),
'toDate' => $now->copy()->subDay()->endOfDay(),
],
self::FREQUENCY_QUARTERLY => [
'fromDate' => $now->copy()->subMonths(3)->startOfDay(),
'toDate' => $now->copy()->subDay()->endOfDay(),
],
default => throw new InvalidArgumentException("Unsupported frequency: {$frequency}"),
};
}
private function formatReportPeriodName(string $frequency, Carbon $from, Carbon $to): string
{
$fromYear = $from->format('Y');
$toYear = $to->format('Y');
$differentYears = $fromYear !== $toYear;
switch ($frequency) {
case self::FREQUENCY_DAILY:
return $from->format('j M Y');
case self::FREQUENCY_QUARTERLY:
// 'Jan-Mar 2025' or 'Nov 2024-Jan 2025' if years differ
$startMonth = $from->format('M');
$endMonth = $to->copy()->subMonth();
$endMonthName = $endMonth->format('M');
$endMonthYear = $endMonth->format('Y');
if ($differentYears) {
return "{$startMonth} {$fromYear} - {$endMonthName} {$endMonthYear}";
}
return "{$startMonth} - {$endMonthName} {$toYear}";
case self::FREQUENCY_MONTHLY:
// 'May 2025' - monthly reports are always within the same year
return $from->format('M Y');
case self::FREQUENCY_WEEKLY:
// '4 - 8 Aug 2025', '27 Oct - 3 Nov 2025', or '28 Dec 2024 - 3 Jan 2025' if years differ
$startDay = $from->format('j');
$endDay = $to->format('j');
$startMonth = $from->format('M');
$endMonth = $to->format('M');
if ($differentYears) {
return "{$startDay} {$startMonth} {$fromYear} - {$endDay} {$endMonth} {$toYear}";
}
if ($startMonth !== $endMonth) {
return "{$startDay} {$startMonth} - {$endDay} {$endMonth} {$toYear}";
}
return "{$startDay} - {$endDay} {$endMonth} {$toYear}";
case self::FREQUENCY_ONE_OFF:
// '2 May-31 May 2025' or '15 Dec 2024-15 Jan 2025' if years differ
$startDay = $from->format('j');
$startMonth = $from->format('M');
$endDay = $to->format('j');
$endMonth = $to->format('M');
// If same month and year, use a format like '2-31 May 2025'
if ($startMonth === $endMonth && ! $differentYears) {
return "{$startDay} - {$endDay} {$startMonth} {$toYear}";
}
// If different years, include both years
if ($differentYears) {
return "{$startDay} {$startMonth} {$fromYear} - {$endDay} {$endMonth} {$toYear}";
}
// Same year but different months
return "{$startDay} {$startMonth} - {$endDay} {$endMonth} {$toYear}";
default:
// Default format for unknown frequencies
return $from->format('j M Y') . ' - ' . $to->format('j M Y');
}
}
public function sanitizeFileName(string $fileName): string
{
return str_replace(['/', '\\'], '-', $fileName);
}
private function getPayload(AutomatedReportsService $automatedReportsService)
{
$reportResult = AutomatedReportResult::find(269);
$automatedReport = $reportResult->getReport();
$activityIds = [1,2,3];
$payload = $automatedReportsService->getAskJiminnyGenerateReportPayload(
automatedReport: $automatedReport,
reportResult: $reportResult,
activityIds: $activityIds,
);
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$payload ' . PHP_EOL . print_r($payload, true));
}
private function rateLimit()
{
$team = Team::find(2);
$config = $team->getCrmConfiguration();
$crmResolver = app(CrmOwnerResolver::class, [
'team' => $team,
'integrationAdmin' => $team->getOwner(),
'providerSlug' => $config->getProviderName(),
]);
$crmService = $crmResolver->prepareCrmService();
for ($i = 0 ; $i < 3; $i++) {
// if ($i % 25 === 0) {
// $this->info("Syncing opportunity {$i}");
$this->info("Matching contact {$i}");
// }
// $crmService->syncOpportunity('374720564');
$crmService->matchByName('Robot');
}
}
private function simulateMatchActivityStorm(int $teamId = 2, int $count = 100): void
{
$team = Team::find($teamId);
$config = $team->getCrmConfiguration();
$activities = Activity::query()
->where('crm_configuration_id', $config->getId())
->orderByDesc('id')
->limit($count)
->get();
$this->info("Dispatching {$activities->count()} MatchActivityCrmData jobs (portal={$config->getId()})");
foreach ($activities as $activity) {
MatchActivityCrmData::dispatch($activity->getId(), $config, true);
}
$this->info('Done. Watch logs and run jiminny:debug observeRateLimit to inspect cache state.');
}
private function simulateVerifyTaskStorm(int $teamId = 2, int $count = 100): void
{
$activities = Activity::query()
->where('team_id', $teamId)
->whereNotNull('crm_provider_id')
->orderByDesc('id')
->limit($count)
->get();
$this->info("Dispatching {$activities->count()} VerifyActivityCrmTaskJob jobs");
foreach ($activities as $activity) {
VerifyActivityCrmTaskJob::dispatch($activity->getId());
}
$this->info('Done.');
}
private function observeRateLimitCache(int $teamId = 2): void
{
$team = Team::find($teamId);
$config = $team->getCrmConfiguration();
$key = sprintf('hubspot:ratelimit:portal:%d', $config->getId());
$value = Redis::get($key);
$ttl = Redis::ttl($key);
$this->info("Redis key: {$key}");
$this->info('Value: ' . ($value ?? '(empty)'));
$this->info("TTL: {$ttl}s");
}
}
Show Replace Field
Search History
429
New Line
Match Case
Words
Regex
Replace History
Replace
New Line
Preserve case
2/2
Previous Occurrence
Next Occurrence
Filter Search Results
Open in Window, Multiple Cursors
Click to highlight
Close
Sync Changes
Hide This Notification
Code changed:
Hide
174
Previous Highlighted Error
Next Highlighted Error
[2026-05-11 11:06:08] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {"activity_id":612562,"team_id":2,"email":"[EMAIL]"} {"correlation_id":"c1949c5e-c0f4-431e-ba26-59d49408d871","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:08] local.INFO: [CrmActivityService] CRM matching completed {"activity_id":612562,"participants_processed":2,"exact_matches":0,"domain_matches":1,"best_match_found":true} {"correlation_id":"c1949c5e-c0f4-431e-ba26-59d49408d871","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:08] local.INFO: [ EsUpdateTarget ] Update single target {"target":"activities","purpose":"searchable-observer-update","entityId":612562} {"correlation_id":"c1949c5e-c0f4-431e-ba26-59d49408d871","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:08] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {"entityType":"activities","entityId":612562,"collectionKey":"activities-for-update-priority","withPriority":true} {"correlation_id":"c1949c5e-c0f4-431e-ba26-59d49408d871","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:08] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {"activity":612562,"remote_search":true,"lead_id":null,"contact_id":null,"account_id":26,"opportunity_id":22,"stage_id":89} {"correlation_id":"c1949c5e-c0f4-431e-ba26-59d49408d871","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:09] local.INFO: [MatchActivityCrmData] Starting CRM data matching {"activity":612561,"remote_search":true,"set_configuration":2,"old_state":{"lead_id":null,"contact_id":null,"account_id":26,"opportunity_id":22,"stage_id":89}} {"correlation_id":"b715f531-04da-410f-be31-055aa1c13754","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:09] local.INFO: [ EsUpdateTarget ] Update single target {"target":"activities","purpose":"searchable-observer-update","entityId":612561} {"correlation_id":"b715f531-04da-410f-be31-055aa1c13754","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:09] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {"entityType":"activities","entityId":612561,"collectionKey":"activities-for-update-priority","withPriority":true} {"correlation_id":"b715f531-04da-410f-be31-055aa1c13754","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:09] local.INFO: [MatchActivityCrmData] Participants old state {"activity":612561,"participants":[{"id":999780,"user_id":206,"contact_id":null,"lead_id":null},{"id":999781,"user_id":null,"contact_id":null,"lead_id":null}]} {"correlation_id":"b715f531-04da-410f-be31-055aa1c13754","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:09] local.INFO: [SocialAccountService] Fetching token {"socialAccountId":1499,"provider":"hubspot"} {"correlation_id":"b715f531-04da-410f-be31-055aa1c13754","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:09] local.INFO: [SocialAccountService] Token retrieved {"socialAccountId":1499,"provider":"hubspot"} {"correlation_id":"b715f531-04da-410f-be31-055aa1c13754","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:09] local.INFO: [EncryptedTokenManager] Generating access token. {"mode":"legacy"} {"correlation_id":"b715f531-04da-410f-be31-055aa1c13754","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:09] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {"crm_provider":"hubspot","crm_owner":148,"team_id":2} {"correlation_id":"b715f531-04da-410f-be31-055aa1c13754","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:09] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {"activity_id":612561,"team_id":2,"email":"[EMAIL]"} {"correlation_id":"b715f531-04da-410f-be31-055aa1c13754","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:09] local.INFO: [Prospect match] Cache miss, calling the API {"identifier_type":"email","identifier":"[EMAIL]"} {"correlation_id":"b715f531-04da-410f-be31-055aa1c13754","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:10] local.INFO: [Hubspot] Failed to fetch contact {"email":"[EMAIL]","reason":"[404] Client error: `GET https://api.hubapi.com/crm/v3/objects/contacts/447700174614.447782589921.OeREojLVnk%40txt.staging.jiminny.com?properties=email%2Cfirstname%2Clastname%2Ccountry%2Cphone%2Cmobilephone%2Cjobtitle%2Chubspot_owner_id%2Cassociatedcompanyid%2Cphoto&archived=0&idProperty=email` resulted in a `404 Not Found` response"} {"correlation_id":"b715f531-04da-410f-be31-055aa1c13754","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:10] local.INFO: [Prospect match] API returned empty result, caching the miss with empty prospect data {"identifier_type":"email","identifier":"[EMAIL]"} {"correlation_id":"b715f531-04da-410f-be31-055aa1c13754","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:10] local.INFO: [Prospect match] Resolved company domain from email {"email":"[EMAIL]","domain":"jiminny.com"} {"correlation_id":"b715f531-04da-410f-be31-055aa1c13754","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:10] local.INFO: [CrmActivityService] CRM matching completed {"activity_id":612561,"participants_processed":2,"exact_matches":0,"domain_matches":1,"best_match_found":true} {"correlation_id":"b715f531-04da-410f-be31-055aa1c13754","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:10] local.INFO: [ EsUpdateTarget ] Update single target {"target":"activities","purpose":"searchable-observer-update","entityId":612561} {"correlation_id":"b715f531-04da-410f-be31-055aa1c13754","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:10] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {"entityType":"activities","entityId":612561,"collectionKey":"activities-for-update-priority","withPriority":true} {"correlation_id":"b715f531-04da-410f-be31-055aa1c13754","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:10] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {"activity":612561,"remote_search":true,"lead_id":null,"contact_id":null,"account_id":26,"opportunity_id":22,"stage_id":89} {"correlation_id":"b715f531-04da-410f-be31-055aa1c13754","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:10] local.INFO: [MatchActivityCrmData] Starting CRM data matching {"activity":612560,"remote_search":true,"set_configuration":2,"old_state":{"lead_id":null,"contact_id":null,"account_id":26,"opportunity_id":22,"stage_id":89}} {"correlation_id":"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:10] local.INFO: [ EsUpdateTarget ] Update single target {"target":"activities","purpose":"searchable-observer-update","entityId":612560} {"correlation_id":"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:10] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {"entityType":"activities","entityId":612560,"collectionKey":"activities-for-update-priority","withPriority":true} {"correlation_id":"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:10] local.INFO: [MatchActivityCrmData] Participants old state {"activity":612560,"participants":[{"id":999778,"user_id":null,"contact_id":null,"lead_id":null},{"id":999779,"user_id":206,"contact_id":null,"lead_id":null}]} {"correlation_id":"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [SocialAccountService] Fetching token {"socialAccountId":1499,"provider":"hubspot"} {"correlation_id":"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [SocialAccountService] Token retrieved {"socialAccountId":1499,"provider":"hubspot"} {"correlation_id":"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [EncryptedTokenManager] Generating access token. {"mode":"legacy"} {"correlation_id":"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {"crm_provider":"hubspot","crm_owner":148,"team_id":2} {"correlation_id":"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [Prospect match] Cache / local search hit {"identifier_type":"email","identifier":"[EMAIL]"} {"correlation_id":"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [Prospect match] cached empty result - no API calls, try next matching method {"identifier_type":"email","identifier":"[EMAIL]"} {"correlation_id":"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [Prospect match] Resolved company domain from email {"email":"[EMAIL]","domain":"jiminny.com"} {"correlation_id":"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {"activity_id":612560,"team_id":2,"email":"[EMAIL]"} {"correlation_id":"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [CrmActivityService] CRM matching completed {"activity_id":612560,"participants_processed":2,"exact_matches":0,"domain_matches":1,"best_match_found":true} {"correlation_id":"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [ EsUpdateTarget ] Update single target {"target":"activities","purpose":"searchable-observer-update","entityId":612560} {"correlation_id":"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [ EsUpdateProcessManager ] Finished updating entities in ES {"worker":"","peak_memory":"99.73 MB","elapsed_seconds":0.74,"update_target":"activities","should_iterate_again":false} {"correlation_id":"38935ae1-fcb3-437e-9c9e-1b7415a0b7e1","trace_id":"325a2f9b-9c66-4223-ac02-1a43723f4315"}
[2026-05-11 11:06:11] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {"entityType":"activities","entityId":612560,"collectionKey":"activities-for-update-priority","withPriority":true} {"correlation_id":"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {"activity":612560,"remote_search":true,"lead_id":null,"contact_id":null,"account_id":26,"opportunity_id":22,"stage_id":89} {"correlation_id":"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [MatchActivityCrmData] Starting CRM data matching {"activity":612559,"remote_search":true,"set_configuration":2,"old_state":{"lead_id":null,"contact_id":97,"account_id":69,"opportunity_id":5011,"stage_id":34}} {"correlation_id":"4d59a59c-38a5-4a1d-9379-6ceaf67b747e","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [ EsUpdateTarget ] Update single target {"target":"activities","purpose":"searchable-observer-update","entityId":612559} {"correlation_id":"4d59a59c-38a5-4a1d-9379-6ceaf67b747e","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {"entityType":"activities","entityId":612559,"collectionKey":"activities-for-update-priority","withPriority":true} {"correlation_id":"4d59a59c-38a5-4a1d-9379-6ceaf67b747e","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [MatchActivityCrmData] Participants old state {"activity":612559,"participants":[{"id":999776,"user_id":206,"contact_id":null,"lead_id":null},{"id":999777,"user_id":null,"contact_id":97,"lead_id":null}]} {"correlation_id":"4d59a59c-38a5-4a1d-9379-6ceaf67b747e","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [SocialAccountService] Fetching token {"socialAccountId":1499,"provider":"hubspot"} {"correlation_id":"4d59a59c-38a5-4a1d-9379-6ceaf67b747e","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [SocialAccountService] Token retrieved {"socialAccountId":1499,"provider":"hubspot"} {"correlation_id":"4d59a59c-38a5-4a1d-9379-6ceaf67b747e","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [EncryptedTokenManager] Generating access token. {"mode":"legacy"} {"correlation_id":"4d59a59c-38a5-4a1d-9379-6ceaf67b747e","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {"crm_provider":"hubspot","crm_owner":148,"team_id":2} {"correlation_id":"4d59a59c-38a5-4a1d-9379-6ceaf67b747e","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {"activity_id":612559,"team_id":2,"email":"[EMAIL]"} {"correlation_id":"4d59a59c-38a5-4a1d-9379-6ceaf67b747e","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: ProspectCache - Searching DB for opportunity by owner {"account_id":69,"contact_id":97,"owner_id":206} {"correlation_id":"4d59a59c-38a5-4a1d-9379-6ceaf67b747e","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: ProspectCache - Fallback DB opportunity search {"account_id":69,"contact_id":97} {"correlation_id":"4d59a59c-38a5-4a1d-9379-6ceaf67b747e","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: ProspectCache - Opportunity DB search results {"account_id":69,"contact_id":97,"opportunity_id":5011} {"correlation_id":"4d59a59c-38a5-4a1d-9379-6ceaf67b747e","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [Prospect match] Cache / local search hit {"identifier_type":"email","identifier":"[EMAIL]"} {"correlation_id":"4d59a59c-38a5-4a1d-9379-6ceaf67b747e","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [CrmActivityService] CRM matching completed {"activity_id":612559,"participants_processed":2,"exact_matches":1,"domain_matches":0,"best_match_found":true} {"correlation_id":"4d59a59c-38a5-4a1d-9379-6ceaf67b747e","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [ EsUpdateTarget ] Update single target {"target":"activities","purpose":"searchable-observer-update","entityId":612559} {"correlation_id":"4d59a59c-38a5-4a1d-9379-6ceaf67b747e","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {"entityType":"activities","entityId":612559,"collectionKey":"activities-for-update-priority","withPriority":true} {"correlation_id":"4d59a59c-38a5-4a1d-9379-6ceaf67b747e","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {"activity":612559,"remote_search":true,"lead_id":null,"contact_id":97,"account_id":69,"opportunity_id":5011,"stage_id":34} {"correlation_id":"4d59a59c-38a5-4a1d-9379-6ceaf67b747e","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:12] local.INFO: [MatchActivityCrmData] Starting CRM data matching {"activity":612558,"remote_search":true,"set_configuration":2,"old_state":{"lead_id":null,"contact_id":97,"account_id":69,"opportunity_id":5011,"stage_id":34}} {"correlation_id":"a105800d-d2f7-4772-a534-7791e262461c","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:12] local.INFO: [ EsUpdateTarget ] Update single target {"target":"activities","purpose":"searchable-observer-update","entityId":612558} {"correlation_id":"a105800d-d2f7-4772-a534-7791e262461c","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:12] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {"entityType":"activities","entityId":612558,"collectionKey":"activities-for-update-priority","withPriority":true} {"correlation_id":"a105800d-d2f7-4772-a534-7791e262461c","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:12] local.INFO: [MatchActivityCrmData] Participants old state {"activity":612558,"participants":[{"id":999774,"user_id":206,"contact_id":null,"lead_id":null},{"id":999775,"user_id":null,"contact_id":97,"lead_id":null}]} {"correlation_id":"a105800d-d2f7-4772-a534-7791e262461c","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:12] local.INFO: [SocialAccountService] Fetching token {"socialAccountId":1499,"provider":"hubspot"} {"correlation_id":"a105800d-d2f7-4772-a534-7791e262461c","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:12] local.INFO: [SocialAccountService] Token retrieved {"socialAccountId":1499,"provider":"hubspot"} {"correlation_id":"a105800d-d2f7-4772-a534-7791e262461c","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:12] local.INFO: [EncryptedTokenManager] Generating access token. {"mode":"legacy"} {"correlation_id":"a105800d-d2f7-4772-a534-7791e262461c","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:12] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {"crm_provider":"hubspot","crm_owner":148,"team_id":2} {"correlation_id":"a105800d-d2f7-4772-a534-7791e262461c","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:12] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {"activity_id":612558,"team_id":2,"email":"[EMAIL]"} {"correlation_id":"a105800d-d2f7-4772-a534-7791e262461c","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:12] local.INFO: [Prospect match] Cache / local search hit {"identifier_type":"email","identifier":"[EMAIL]"} {"correlation_id":"a105800d-d2f7-4772-a534-7791e262461c","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:12] local.INFO: [CrmActivityService] CRM matching completed {"activity_id":612558,"participants_processed":2,"exact_matches":1,"domain_matches":0,"best_match_found":true} {"correlation_id":"a105800d-d2f7-4772-a534-7791e262461c","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:12] local.INFO: [ EsUpdateTarget ] Update single target {"target":"activities","purpose":"searchable-observer-update","entityId":612558} {"correlation_id":"a105800d-d2f7-4772-a534-7791e262461c","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:12] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {"entityType":"activities","entityId":612558,"collectionKey":"activities-for-update-priority","withPriority":true} {"correlation_id":"a105800d-d2f7-4772-a534-7791e262461c","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:12] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {"activity":612558,"remote_search":true,"lead_id":null,"contact_id":97,"account_id":69,"opportunity_id":5011,"stage_id":34} {"correlation_id":"a105800d-d2f7-4772-a534-7791e262461c","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:13] local.INFO: [MatchActivityCrmData] Starting CRM data matching {"activity":612557,"remote_search":true,"set_configuration":2,"old_state":{"lead_id":null,"contact_id":97,"account_id":69,"opportunity_id":5011,"stage_id":34}} {"correlation_id":"3ec57437-5796-4e44-8bf0-aaec149ac813","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:13] local.INFO: [ EsUpdateTarget ] Update single target {"target":"activities","purpose":"searchable-observer-update","entityId":612557} {"correlation_id":"3ec57437-5796-4e44-8bf0-aaec149ac813","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:13] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {"entityType":"activities","entityId":612557,"collectionKey":"activities-for-update-priority","withPriority":true} {"correlation_id":"3ec57437-5796-4e44-8bf0-aaec149ac813","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:13] local.INFO: [MatchActivityCrmData] Participants old state {"activity":612557,"participants":[{"id":999772,"user_id":206,"contact_id":null,"lead_id":null},{"id":999773,"user_id":null,"contact_id":97,"lead_id":null}]} {"correlation_id":"3ec57437-5796-4e44-8bf0-aaec149ac813","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:13] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"mailbox:skip-lists:refresh","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.727} {"correlation_id":"5203040f-23be-43e6-8410-ba49ee1b38b4","trace_id":"e787953d-9b78-4b63-a987-4f93f6892984"}
[2026-05-11 11:06:13] local.INFO: [SocialAccountService] Fetching token {"socialAccountId":1499,"provider":"hubspot"} {"correlation_id":"3ec57437-5796-4e44-8bf0-aaec149ac813","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:13] local.INFO: [SocialAccountService] Token retrieved {"socialAccountId":1499,"provider":"hubspot"} {"correlation_id":"3ec57437-5796-4e44-8bf0-aaec149ac813","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:13] local.INFO: [EncryptedTokenManager] Generating access token. {"mode":"legacy"} {"correlation_id":"3ec57437-5796-4e44-8bf0-aaec149ac813","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:13] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {"crm_provider":"hubspot","crm_owner":148,"team_id":2} {"correlation_id":"3ec57437-5796-4e44-8bf0-aaec149ac813","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:13] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {"activity_id":612557,"team_id":2,"email":"[EMAIL]"} {"correlation_id":"3ec57437-5796-4e44-8bf0-aaec149ac813","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:13] local.INFO: [Prospect match] Cache / local search hit {"identifier_type":"email","identifier":"[EMAIL]"} {"correlation_id":"3ec57437-5796-4e44-8bf0-aaec149ac813","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:13] local.INFO: [CrmActivityService] CRM matching completed {"activity_id":612557,"participants_processed":2,"exact_matches":1,"domain_matches":0,"best_match_found":true} {"correlation_id":"3ec57437-5796-4e44-8bf0-aaec149ac813","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:13] local.INFO: [ EsUpdateTarget ] Update single target {"target":"activities","purpose":"searchable-observer-update","entityId":612557} {"correlation_id":"3ec57437-5796-4e44-8bf0-aaec149ac813","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:13] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {"entityType":"activities","entityId":612557,"collectionKey":"activities-for-update-priority","withPriority":true} {"correlation_id":"3ec57437-5796-4e44-8bf0-aaec149ac813","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:13] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {"activity":612557,"remote_search":true,"lead_id":null,"contact_id":97,"account_id":69,"opportunity_id":5011,"stage_id":34} {"correlation_id":"3ec57437-5796-4e44-8bf0-aaec149ac813","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:13] local.INFO: Jiminny\Console\Commands\Command::run Memory usage for command {"command":"mailbox:skip-lists:refresh","memoryBeforeCommandInMb":60.0,"memoryAfterCommandInMB":60.0,"memoryPeakBeforeCommandInMb":99.727,"memoryPeakAfterCommandInMB":99.727} {"correlation_id":"5203040f-23be-43e6-8410-ba49ee1b38b4","trace_id":"e787953d-9b78-4b63-a987-4f93f6892984"}
[2026-05-11 11:06:13] local.INFO: [MatchActivityCrmData] Starting CRM data matching {"activity":612556,"remote_search":true,"set_configuration":2,"old_state":{"lead_id":null,"contact_id":97,"account_id":69,"opportunity_id":5011,"stage_id":34}} {"correlation_id":"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:13] local.INFO: [ EsUpdateTarget ] Update single target {"target":"activities","purpose":"searchable-observer-update","entityId":612556} {"correlation_id":"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:13] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {"entityType":"activities","entityId":612556,"collectionKey":"activities-for-update-priority","withPriority":true} {"correlation_id":"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:13] local.INFO: [MatchActivityCrmData] Participants old state {"activity":612556,"participants":[{"id":999770,"user_id":206,"contact_id":null,"lead_id":null},{"id":999771,"user_id":null,"contact_id":97,"lead_id":null}]} {"correlation_id":"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:13] local.INFO: [SocialAccountService] Fetching token {"socialAccountId":1499,"provider":"hubspot"} {"correlation_id":"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:13] local.INFO: [SocialAccountService] Token retrieved {"socialAccountId":1499,"provider":"hubspot"} {"correlation_id":"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:13] local.INFO: [EncryptedTokenManager] Generating access token. {"mode":"legacy"} {"correlation_id":"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:13] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {"crm_provider":"hubspot","crm_owner":148,"team_id":2} {"correlation_id":"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:14] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {"activity_id":612556,"team_id":2,"email":"[EMAIL]"} {"correlation_id":"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:14] local.INFO: [Prospect match] Cache / local search hit {"identifier_type":"email","identifier":"[EMAIL]"} {"correlation_id":"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:14] local.INFO: [CrmActivityService] CRM matching completed {"activity_id":612556,"participants_processed":2,"exact_matches":1,"domain_matches":0,"best_match_found":true} {"correlation_id":"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:14] local.INFO: [ EsUpdateTarget ] Update single target {"target":"activities","purpose":"searchable-observer-update","entityId":612556} {"correlation_id":"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:14] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {"entityType":"activities","entityId":612556,"collectionKey":"activities-for-update-priority","withPriority":true} {"correlation_id":"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:14] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {"activity":612556,"remote_search":true,"lead_id":null,"contact_id":97,"account_id":69,"opportunity_id":5011,"stage_id":34} {"correlation_id":"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:14] local.INFO: [MatchActivityCrmData] Starting CRM data matching {"activity":612555,"remote_search":true,"set_configuration":2,"old_state":{"lead_id":null,"contact_id":97,"account_id":69,"opportunity_id":5011,"stage_id":34}} {"correlation_id":"3e9d1a70-5491-44ae-8f28-5cb9b60e1859","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:14] local.INFO: [ EsUpdateTarget ] Update single target {"target":"activities","purpose":"searchable-observer-update","entityId":612555} {"correlation_id":"3e9d1a70-5491-44ae-8f28-5cb9b60e1859","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:14] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {"entityType":"activities","entityId":612555,"collectionKey":"activities-for-update-priority","withPriority":true} {"correlation_id":"3e9d1a70-5491-44ae-8f28-5cb9b60e1859","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:14] local.INFO: [MatchActivityCrmData] Participants old state {"activity":612555,"participants":[{"id":999768,"user_id":206,"contact_id":null,"lead_id":null},{"id":999769,"user_id":null,"contact_id":97,"lead_id":null}]} {"correlation_id":"3e9d1a70-5491-44ae-8f28-5cb9b60e1859","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:14] local.INFO: [SocialAccountService] Fetching token {"socialAccountId":1499,"provider":"hubspot"} {"correlation_id":"3e9d1a70-5491-44ae-8f28-5cb9b60e1859","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:14] local.INFO: [SocialAccountService] Token retrieved {"socialAccountId":1499,"provider":"hubspot"} {"correlation_id":"3e9d1a70-5491-44ae-8f28-5cb9b60e1859","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:14] local.INFO: [EncryptedTokenManager] Generating access token. {"mode":"legacy"} {"correlation_id":"3e9d1a70-5491-44ae-8f28-5cb9b60e1859","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:14] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {"crm_provider":"hubspot","crm_owner":148,"team_id":2} {"correlation_id":"3e9d1a70-5491-44ae-8f28-5cb9b60e1859","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:14] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {"activity_id":612555,"team_id":2,"email":"[EMAIL]"} {"correlation_id":"3e9d1a70-5491-44ae-8f28-5cb9b60e1859","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:14] local.INFO: [Prospect match] Cache / local search hit {"identifier_type":"email","identifier":"[EMAIL]"} {"correlation_id":"3e9d1a70-5491-44ae-8f28-5cb9b60e1859","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:14] local.INFO: [CrmActivityService] CRM matching completed {"activity_id":612555,"participants_processed":2,"exact_matches":1,"domain_matches":0,"best_match_found":true} {"correlation_id":"3e9d1a70-5491-44ae-8f28-5cb9b60e1859","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:14] local.INFO: [ EsUpdateTarget ] Update single target {"target":"activities","purpose":"searchable-observer-update","entityId":612555} {"correlation_id":"3e9d1a70-5491-44ae-8f28-5cb9b60e1859","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:14] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {"entityType":"activities","entityId":612555,"collectionKey":"activities-for-update-priority","withPriority":true} {"correlation_id":"3e9d1a70-5491-44ae-8f28-5cb9b60e1859","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:14] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {"activity":612555,"remote_search":true,"lead_id":null,"contact_id":97,"account_id":69,"opportunity_id":5011,"stage_id":34} {"correlation_id":"3e9d1a70-5491-44ae-8f28-5cb9b60e1859","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:14] local.INFO: [MatchActivityCrmData] Starting CRM data matching {"activity":612554,"remote_search":true,"set_configuration":2,"old_state":{"lead_id":null,"contact_id":97,"account_id":69,"opportunity_id":5011,"stage_id":34}} {"correlation_id":"7101e979-3a7e-4b2c-bf04-dc6a24ba1356","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:14] local.INFO: [ EsUpdateTarget ] Update single target {"target":"activities","purpose":"searchable-observer-update","entityId":612554} {"correlation_id":"7101e979-3a7e-4b2c-bf04-dc6a24ba1356","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:14] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {"entityType":"activities","entityId":612554,"collectionKey":"activities-for-update-priority",...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"bounds":{"left":0.025930852,"top":0.019952115,"width":0.03856383,"height":0.025538707},"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JY-20725-handle-HS-search-rate-limit, menu","depth":5,"bounds":{"left":0.064494684,"top":0.019952115,"width":0.09541223,"height":0.025538707},"on_screen":true,"help_text":"Git Branch: JY-20725-handle-HS-search-rate-limit","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"bounds":{"left":0.82413566,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"HandleHubspotRateLimitTest","depth":6,"bounds":{"left":0.8394282,"top":0.019952115,"width":0.076130316,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'HandleHubspotRateLimitTest'","depth":6,"bounds":{"left":0.9155585,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'HandleHubspotRateLimitTest'","depth":6,"bounds":{"left":0.9268617,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"bounds":{"left":0.9381649,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"bounds":{"left":0.96609044,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"bounds":{"left":0.9773936,"top":0.019952115,"width":0.011303191,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"bounds":{"left":0.9886968,"top":0.019952115,"width":0.011303186,"height":0.025538707},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"5","depth":4,"bounds":{"left":0.5043218,"top":0.17478053,"width":0.007978723,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"133","depth":4,"bounds":{"left":0.5142952,"top":0.17478053,"width":0.011968086,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXStaticText","text":"11","depth":4,"bounds":{"left":0.52825797,"top":0.17478053,"width":0.008976064,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.53889626,"top":0.17318435,"width":0.00731383,"height":0.018355945},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.5462101,"top":0.17318435,"width":0.006981383,"height":0.018355945},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Console\\Commands;\n\nuse Carbon\\Carbon;\nuse Carbon\\CarbonImmutable;\nuse Illuminate\\Console\\Command;\nuse Illuminate\\Support\\Facades\\Redis;\nuse InvalidArgumentException;\nuse Jiminny\\Jobs\\AutomatedReports\\RequestGenerateAskJiminnyReportJob;\nuse Jiminny\\Jobs\\AutomatedReports\\SendReportMailJob;\nuse Jiminny\\Jobs\\Crm\\Delete\\VerifyActivityCrmTaskJob;\nuse Jiminny\\Jobs\\Crm\\MatchActivityCrmData;\nuse Jiminny\\Jobs\\JobDispatcherInterface;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\AutomatedReportResult;\nuse Jiminny\\Models\\Team;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Repositories\\AutomatedReportsRepository;\nuse Jiminny\\Services\\Activity\\CrmOwnerResolver;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\n\n/**\n * Class JiminnyDebugCommand\n *\n * @package Jiminny\\Console\\Commands\n */\nclass JiminnyDebugCommand extends Command\n{\n public const string FREQUENCY_DAILY = 'daily';\n public const string FREQUENCY_WEEKLY = 'weekly';\n public const string FREQUENCY_MONTHLY = 'monthly';\n public const string FREQUENCY_QUARTERLY = 'quarterly';\n public const string FREQUENCY_ONE_OFF = 'one_off';\n protected $signature = 'jiminny:debug';\n\n public function handle(\n JobDispatcherInterface $jobDispatcher,\n AutomatedReportsService $automatedReportsService,\n AutomatedReportsRepository $automatedReportsRepository,\n UserPilotClient $userPilotClient\n ): void {\n // Choose ONE of the following to run, then comment out the others.\n // 1) Dispatch a storm of MatchActivityCrmData jobs against team 2\n $this->simulateMatchActivityStorm(teamId: 2, count: 100);\n\n // 2) Dispatch a storm of VerifyActivityCrmTaskJob jobs (simulates DeleteCrmEntityTrait fan-out)\n // $this->simulateVerifyTaskStorm(teamId: 2, count: 100);\n\n // 3) Inspect Redis circuit-breaker state for the team's HubSpot portal\n // $this->observeRateLimitCache(teamId: 2);\n\n // 4) Make 3 synchronous matchByName calls (foreground, hits API directly)\n // $this->rateLimit();\n exit(1);\n\n\n\n $report = AutomatedReport::find(71);\n $last = AutomatedReportResult::query()\n ->where('report_id', $report->getId())\n ->whereIn('status', [AutomatedReportResult::STATUS_DEFAULT, AutomatedReportResult::STATUS_FAILED])\n// ->where('reason', '!=', AutomatedReportResult::REASON_NOT_ENOUGH_ACTIVITIES)\n ->whereDate('created_at', CarbonImmutable::now()->toDateString())\n ->latest()\n ->first();\n\n $this->info(\"Last: {$last->getId()}\");\n\n exit(1);\n\n $user = User::find(143);\n // $count = $automatedReportsRepository->countUserReports($user);\n // $this->info(\"Count: {$count}\");\n // $count = $automatedReportsRepository->countAllUserReports($user);\n // $this->info(\"All count: {$count}\");\n\n $payload = [\n 'report_type' => 'ask_jiminny',\n 'frequency' => 'weekly',\n ];\n $userPilotClient->track($user, 'ask-jiminny-report-generated', $payload);\n\n exit(1);\n\n $now = Carbon::now()->subDay(1);\n $this->info(\"Now: {$now->toDateTimeString()}\");\n $weekStart = Carbon::getWeekStartsAt();\n $this->info(\"Now: {$weekStart}\");\n\n // $from = $now->copy()->previousWeekday()->startOfDay();\n // $to = $now->copy()->previousWeekday()->endOfDay();\n\n // $fromOld = $now->copy()->subWeeks(1)->startOfDay();\n // $toOld = $now->copy()->subDay()->endOfDay();\n // $fromNew = $now->copy()->subWeek()->startOfWeek();\n // $toNew = $now->copy()->subWeek()->endOfWeek();\n\n // $fromOld = $now->copy()->subMonths(1)->startOfDay();\n // $toOld = $now->copy()->subDay()->endOfDay();\n // $fromNew = $now->copy()->subMonthNoOverflow()->startOfMonth();\n // $toNew = $now->copy()->subMonthNoOverflow()->endOfMonth();\n\n $fromOld = $now->copy()->subMonths(3)->startOfDay();\n $toOld = $now->copy()->subDay()->endOfDay();\n $fromNew = $now->copy()->subQuarterNoOverflow()->startOfQuarter();\n $toNew = $now->copy()->subQuarterNoOverflow()->endOfQuarter();\n\n $this->info(\"From old: {$fromOld->toDateTimeString()}\");\n $this->info(\"To old: {$toOld->toDateTimeString()}\");\n $this->info(\"From new: {$fromNew->toDateTimeString()}\");\n $this->info(\"To new: {$toNew->toDateTimeString()}\");\n\n exit(1);\n\n $report = AutomatedReport::find(71);\n\n $job = new RequestGenerateAskJiminnyReportJob($report->getUuid());\n $jobDispatcher->dispatch($job);\n\n exit(1);\n\n\n // $this->formatDate($jobDispatcher);\n // $this->sendMail($jobDispatcher, $automatedReportsService);\n // $this->crmService();\n\n $this->getPayload($automatedReportsService);\n\n exit(1);\n }\n\n\n\n private function crmService()\n {\n $activity = Activity::find(418141);\n\n $team = Team::find(19);\n $config = $team->getCrmConfiguration();\n\n $crmResolver = app(CrmOwnerResolver::class, [\n 'team' => $team,\n 'integrationAdmin' => $team->getOwner(),\n 'providerSlug' => $config->getProviderName(),\n ]);\n\n $crmService = $crmResolver->prepareCrmService();\n\n $crmService->createTranscriptNotes($activity);\n }\n\n private function sendMail(JobDispatcherInterface $jobDispatcher, AutomatedReportsService $automatedReportsService)\n {\n $reportUuid = '';\n // $report = $automatedReportsService->getReportResult($reportUuid);\n $report = AutomatedReportResult::find(275);\n $validRecipients = $automatedReportsService->getValidRecipientUsers(\n $report->getReport(),\n includeJiminny: true,\n );\n\n $recipient = $validRecipients[0];\n\n $fileName = $automatedReportsService->getReportFileName($report);\n $typeName = $report->getReport()->getCustomName()\n ?? $automatedReportsService->getReportTypeName($report);\n $teamsName = $automatedReportsService->getReportTeamsName($report);\n $periodName = $automatedReportsService->getReportPeriodName($report);\n $s3Path = $automatedReportsService->getMediaPath($report);\n\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$fileName ' . PHP_EOL . print_r($fileName, true));\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$typeName ' . PHP_EOL . print_r($typeName, true));\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$teamsName ' . PHP_EOL . print_r($teamsName, true));\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$periodName ' . PHP_EOL . print_r($periodName, true));\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$s3Path ' . PHP_EOL . print_r($s3Path, true));\n\n $jobDispatcher->dispatch(\n new SendReportMailJob(\n reportUuid: $report->getUuid(),\n s3Path: $s3Path,\n recipientEmail: $recipient['email'],\n recipientName: $recipient['name'] ?? null,\n fileName: $fileName,\n typeName: $typeName,\n teamsName: $teamsName,\n periodName: $periodName,\n isAskJiminny: true,\n )\n );\n\n exit(1);\n }\n\n private function formatDate(JobDispatcherInterface $jobDispatcher): void\n {\n $customName = 'Custom report name';\n // $frequency = self::FREQUENCY_DAILY;\n // $frequency = self::FREQUENCY_WEEKLY;\n $frequency = self::FREQUENCY_MONTHLY;\n // $frequency = self::FREQUENCY_QUARTERLY;\n // $frequency = self::FREQUENCY_ONE_OFF;\n $period = $this->calculateFromAndToDatePeriod($frequency);\n $from = $period['fromDate'];\n $to = $period['toDate'];\n $periodName = $this->formatReportPeriodName($frequency, $from, $to);\n $filenameSuffix = null;\n\n if ($customName) {\n if ($filenameSuffix) {\n $customName .= \" {$filenameSuffix}\";\n }\n\n $result = $this->sanitizeFileName(\"{$customName} - {$periodName}\");\n }\n\n $this->info($result);\n }\n\n public function calculateFromAndToDatePeriod(\n string $frequency,\n ?Carbon $fromDate = null,\n ?Carbon $toDate = null\n ): array {\n if ($frequency === self::FREQUENCY_ONE_OFF) {\n return [\n 'fromDate' => $fromDate,\n 'toDate' => $toDate,\n ];\n }\n\n $now = Carbon::now();\n\n return match ($frequency) {\n self::FREQUENCY_DAILY => [\n 'fromDate' => $now->copy()->subDay()->startOfDay(),\n 'toDate' => $now->copy()->subDay()->endOfDay(),\n ],\n self::FREQUENCY_WEEKLY => [\n 'fromDate' => $now->copy()->subWeeks(1)->startOfDay(),\n 'toDate' => $now->copy()->subDay()->endOfDay(),\n ],\n self::FREQUENCY_MONTHLY => [\n 'fromDate' => $now->copy()->subMonths(1)->startOfDay(),\n 'toDate' => $now->copy()->subDay()->endOfDay(),\n ],\n self::FREQUENCY_QUARTERLY => [\n 'fromDate' => $now->copy()->subMonths(3)->startOfDay(),\n 'toDate' => $now->copy()->subDay()->endOfDay(),\n ],\n default => throw new InvalidArgumentException(\"Unsupported frequency: {$frequency}\"),\n };\n }\n\n private function formatReportPeriodName(string $frequency, Carbon $from, Carbon $to): string\n {\n $fromYear = $from->format('Y');\n $toYear = $to->format('Y');\n $differentYears = $fromYear !== $toYear;\n\n switch ($frequency) {\n case self::FREQUENCY_DAILY:\n return $from->format('j M Y');\n\n case self::FREQUENCY_QUARTERLY:\n // 'Jan-Mar 2025' or 'Nov 2024-Jan 2025' if years differ\n $startMonth = $from->format('M');\n $endMonth = $to->copy()->subMonth();\n $endMonthName = $endMonth->format('M');\n $endMonthYear = $endMonth->format('Y');\n\n if ($differentYears) {\n return \"{$startMonth} {$fromYear} - {$endMonthName} {$endMonthYear}\";\n }\n\n return \"{$startMonth} - {$endMonthName} {$toYear}\";\n\n case self::FREQUENCY_MONTHLY:\n // 'May 2025' - monthly reports are always within the same year\n return $from->format('M Y');\n\n case self::FREQUENCY_WEEKLY:\n // '4 - 8 Aug 2025', '27 Oct - 3 Nov 2025', or '28 Dec 2024 - 3 Jan 2025' if years differ\n $startDay = $from->format('j');\n $endDay = $to->format('j');\n $startMonth = $from->format('M');\n $endMonth = $to->format('M');\n\n if ($differentYears) {\n return \"{$startDay} {$startMonth} {$fromYear} - {$endDay} {$endMonth} {$toYear}\";\n }\n\n if ($startMonth !== $endMonth) {\n return \"{$startDay} {$startMonth} - {$endDay} {$endMonth} {$toYear}\";\n }\n\n return \"{$startDay} - {$endDay} {$endMonth} {$toYear}\";\n\n case self::FREQUENCY_ONE_OFF:\n // '2 May-31 May 2025' or '15 Dec 2024-15 Jan 2025' if years differ\n $startDay = $from->format('j');\n $startMonth = $from->format('M');\n $endDay = $to->format('j');\n $endMonth = $to->format('M');\n\n // If same month and year, use a format like '2-31 May 2025'\n if ($startMonth === $endMonth && ! $differentYears) {\n return \"{$startDay} - {$endDay} {$startMonth} {$toYear}\";\n }\n\n // If different years, include both years\n if ($differentYears) {\n return \"{$startDay} {$startMonth} {$fromYear} - {$endDay} {$endMonth} {$toYear}\";\n }\n\n // Same year but different months\n return \"{$startDay} {$startMonth} - {$endDay} {$endMonth} {$toYear}\";\n\n default:\n // Default format for unknown frequencies\n return $from->format('j M Y') . ' - ' . $to->format('j M Y');\n }\n }\n\n public function sanitizeFileName(string $fileName): string\n {\n return str_replace(['/', '\\\\'], '-', $fileName);\n }\n\n private function getPayload(AutomatedReportsService $automatedReportsService)\n {\n $reportResult = AutomatedReportResult::find(269);\n $automatedReport = $reportResult->getReport();\n $activityIds = [1,2,3];\n $payload = $automatedReportsService->getAskJiminnyGenerateReportPayload(\n automatedReport: $automatedReport,\n reportResult: $reportResult,\n activityIds: $activityIds,\n );\n\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$payload ' . PHP_EOL . print_r($payload, true));\n }\n\n private function rateLimit()\n {\n $team = Team::find(2);\n $config = $team->getCrmConfiguration();\n\n $crmResolver = app(CrmOwnerResolver::class, [\n 'team' => $team,\n 'integrationAdmin' => $team->getOwner(),\n 'providerSlug' => $config->getProviderName(),\n ]);\n\n $crmService = $crmResolver->prepareCrmService();\n\n for ($i = 0 ; $i < 3; $i++) {\n// if ($i % 25 === 0) {\n// $this->info(\"Syncing opportunity {$i}\");\n $this->info(\"Matching contact {$i}\");\n// }\n// $crmService->syncOpportunity('374720564');\n $crmService->matchByName('Robot');\n }\n }\n\n private function simulateMatchActivityStorm(int $teamId = 2, int $count = 100): void\n {\n $team = Team::find($teamId);\n $config = $team->getCrmConfiguration();\n\n $activities = Activity::query()\n ->where('crm_configuration_id', $config->getId())\n ->orderByDesc('id')\n ->limit($count)\n ->get();\n\n $this->info(\"Dispatching {$activities->count()} MatchActivityCrmData jobs (portal={$config->getId()})\");\n\n foreach ($activities as $activity) {\n MatchActivityCrmData::dispatch($activity->getId(), $config, true);\n }\n\n $this->info('Done. Watch logs and run jiminny:debug observeRateLimit to inspect cache state.');\n }\n\n private function simulateVerifyTaskStorm(int $teamId = 2, int $count = 100): void\n {\n $activities = Activity::query()\n ->where('team_id', $teamId)\n ->whereNotNull('crm_provider_id')\n ->orderByDesc('id')\n ->limit($count)\n ->get();\n\n $this->info(\"Dispatching {$activities->count()} VerifyActivityCrmTaskJob jobs\");\n\n foreach ($activities as $activity) {\n VerifyActivityCrmTaskJob::dispatch($activity->getId());\n }\n\n $this->info('Done.');\n }\n\n private function observeRateLimitCache(int $teamId = 2): void\n {\n $team = Team::find($teamId);\n $config = $team->getCrmConfiguration();\n $key = sprintf('hubspot:ratelimit:portal:%d', $config->getId());\n\n $value = Redis::get($key);\n $ttl = Redis::ttl($key);\n\n $this->info(\"Redis key: {$key}\");\n $this->info('Value: ' . ($value ?? '(empty)'));\n $this->info(\"TTL: {$ttl}s\");\n }\n}","depth":4,"on_screen":true,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Console\\Commands;\n\nuse Carbon\\Carbon;\nuse Carbon\\CarbonImmutable;\nuse Illuminate\\Console\\Command;\nuse Illuminate\\Support\\Facades\\Redis;\nuse InvalidArgumentException;\nuse Jiminny\\Jobs\\AutomatedReports\\RequestGenerateAskJiminnyReportJob;\nuse Jiminny\\Jobs\\AutomatedReports\\SendReportMailJob;\nuse Jiminny\\Jobs\\Crm\\Delete\\VerifyActivityCrmTaskJob;\nuse Jiminny\\Jobs\\Crm\\MatchActivityCrmData;\nuse Jiminny\\Jobs\\JobDispatcherInterface;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\AutomatedReportResult;\nuse Jiminny\\Models\\Team;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Repositories\\AutomatedReportsRepository;\nuse Jiminny\\Services\\Activity\\CrmOwnerResolver;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\n\n/**\n * Class JiminnyDebugCommand\n *\n * @package Jiminny\\Console\\Commands\n */\nclass JiminnyDebugCommand extends Command\n{\n public const string FREQUENCY_DAILY = 'daily';\n public const string FREQUENCY_WEEKLY = 'weekly';\n public const string FREQUENCY_MONTHLY = 'monthly';\n public const string FREQUENCY_QUARTERLY = 'quarterly';\n public const string FREQUENCY_ONE_OFF = 'one_off';\n protected $signature = 'jiminny:debug';\n\n public function handle(\n JobDispatcherInterface $jobDispatcher,\n AutomatedReportsService $automatedReportsService,\n AutomatedReportsRepository $automatedReportsRepository,\n UserPilotClient $userPilotClient\n ): void {\n // Choose ONE of the following to run, then comment out the others.\n // 1) Dispatch a storm of MatchActivityCrmData jobs against team 2\n $this->simulateMatchActivityStorm(teamId: 2, count: 100);\n\n // 2) Dispatch a storm of VerifyActivityCrmTaskJob jobs (simulates DeleteCrmEntityTrait fan-out)\n // $this->simulateVerifyTaskStorm(teamId: 2, count: 100);\n\n // 3) Inspect Redis circuit-breaker state for the team's HubSpot portal\n // $this->observeRateLimitCache(teamId: 2);\n\n // 4) Make 3 synchronous matchByName calls (foreground, hits API directly)\n // $this->rateLimit();\n exit(1);\n\n\n\n $report = AutomatedReport::find(71);\n $last = AutomatedReportResult::query()\n ->where('report_id', $report->getId())\n ->whereIn('status', [AutomatedReportResult::STATUS_DEFAULT, AutomatedReportResult::STATUS_FAILED])\n// ->where('reason', '!=', AutomatedReportResult::REASON_NOT_ENOUGH_ACTIVITIES)\n ->whereDate('created_at', CarbonImmutable::now()->toDateString())\n ->latest()\n ->first();\n\n $this->info(\"Last: {$last->getId()}\");\n\n exit(1);\n\n $user = User::find(143);\n // $count = $automatedReportsRepository->countUserReports($user);\n // $this->info(\"Count: {$count}\");\n // $count = $automatedReportsRepository->countAllUserReports($user);\n // $this->info(\"All count: {$count}\");\n\n $payload = [\n 'report_type' => 'ask_jiminny',\n 'frequency' => 'weekly',\n ];\n $userPilotClient->track($user, 'ask-jiminny-report-generated', $payload);\n\n exit(1);\n\n $now = Carbon::now()->subDay(1);\n $this->info(\"Now: {$now->toDateTimeString()}\");\n $weekStart = Carbon::getWeekStartsAt();\n $this->info(\"Now: {$weekStart}\");\n\n // $from = $now->copy()->previousWeekday()->startOfDay();\n // $to = $now->copy()->previousWeekday()->endOfDay();\n\n // $fromOld = $now->copy()->subWeeks(1)->startOfDay();\n // $toOld = $now->copy()->subDay()->endOfDay();\n // $fromNew = $now->copy()->subWeek()->startOfWeek();\n // $toNew = $now->copy()->subWeek()->endOfWeek();\n\n // $fromOld = $now->copy()->subMonths(1)->startOfDay();\n // $toOld = $now->copy()->subDay()->endOfDay();\n // $fromNew = $now->copy()->subMonthNoOverflow()->startOfMonth();\n // $toNew = $now->copy()->subMonthNoOverflow()->endOfMonth();\n\n $fromOld = $now->copy()->subMonths(3)->startOfDay();\n $toOld = $now->copy()->subDay()->endOfDay();\n $fromNew = $now->copy()->subQuarterNoOverflow()->startOfQuarter();\n $toNew = $now->copy()->subQuarterNoOverflow()->endOfQuarter();\n\n $this->info(\"From old: {$fromOld->toDateTimeString()}\");\n $this->info(\"To old: {$toOld->toDateTimeString()}\");\n $this->info(\"From new: {$fromNew->toDateTimeString()}\");\n $this->info(\"To new: {$toNew->toDateTimeString()}\");\n\n exit(1);\n\n $report = AutomatedReport::find(71);\n\n $job = new RequestGenerateAskJiminnyReportJob($report->getUuid());\n $jobDispatcher->dispatch($job);\n\n exit(1);\n\n\n // $this->formatDate($jobDispatcher);\n // $this->sendMail($jobDispatcher, $automatedReportsService);\n // $this->crmService();\n\n $this->getPayload($automatedReportsService);\n\n exit(1);\n }\n\n\n\n private function crmService()\n {\n $activity = Activity::find(418141);\n\n $team = Team::find(19);\n $config = $team->getCrmConfiguration();\n\n $crmResolver = app(CrmOwnerResolver::class, [\n 'team' => $team,\n 'integrationAdmin' => $team->getOwner(),\n 'providerSlug' => $config->getProviderName(),\n ]);\n\n $crmService = $crmResolver->prepareCrmService();\n\n $crmService->createTranscriptNotes($activity);\n }\n\n private function sendMail(JobDispatcherInterface $jobDispatcher, AutomatedReportsService $automatedReportsService)\n {\n $reportUuid = '';\n // $report = $automatedReportsService->getReportResult($reportUuid);\n $report = AutomatedReportResult::find(275);\n $validRecipients = $automatedReportsService->getValidRecipientUsers(\n $report->getReport(),\n includeJiminny: true,\n );\n\n $recipient = $validRecipients[0];\n\n $fileName = $automatedReportsService->getReportFileName($report);\n $typeName = $report->getReport()->getCustomName()\n ?? $automatedReportsService->getReportTypeName($report);\n $teamsName = $automatedReportsService->getReportTeamsName($report);\n $periodName = $automatedReportsService->getReportPeriodName($report);\n $s3Path = $automatedReportsService->getMediaPath($report);\n\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$fileName ' . PHP_EOL . print_r($fileName, true));\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$typeName ' . PHP_EOL . print_r($typeName, true));\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$teamsName ' . PHP_EOL . print_r($teamsName, true));\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$periodName ' . PHP_EOL . print_r($periodName, true));\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$s3Path ' . PHP_EOL . print_r($s3Path, true));\n\n $jobDispatcher->dispatch(\n new SendReportMailJob(\n reportUuid: $report->getUuid(),\n s3Path: $s3Path,\n recipientEmail: $recipient['email'],\n recipientName: $recipient['name'] ?? null,\n fileName: $fileName,\n typeName: $typeName,\n teamsName: $teamsName,\n periodName: $periodName,\n isAskJiminny: true,\n )\n );\n\n exit(1);\n }\n\n private function formatDate(JobDispatcherInterface $jobDispatcher): void\n {\n $customName = 'Custom report name';\n // $frequency = self::FREQUENCY_DAILY;\n // $frequency = self::FREQUENCY_WEEKLY;\n $frequency = self::FREQUENCY_MONTHLY;\n // $frequency = self::FREQUENCY_QUARTERLY;\n // $frequency = self::FREQUENCY_ONE_OFF;\n $period = $this->calculateFromAndToDatePeriod($frequency);\n $from = $period['fromDate'];\n $to = $period['toDate'];\n $periodName = $this->formatReportPeriodName($frequency, $from, $to);\n $filenameSuffix = null;\n\n if ($customName) {\n if ($filenameSuffix) {\n $customName .= \" {$filenameSuffix}\";\n }\n\n $result = $this->sanitizeFileName(\"{$customName} - {$periodName}\");\n }\n\n $this->info($result);\n }\n\n public function calculateFromAndToDatePeriod(\n string $frequency,\n ?Carbon $fromDate = null,\n ?Carbon $toDate = null\n ): array {\n if ($frequency === self::FREQUENCY_ONE_OFF) {\n return [\n 'fromDate' => $fromDate,\n 'toDate' => $toDate,\n ];\n }\n\n $now = Carbon::now();\n\n return match ($frequency) {\n self::FREQUENCY_DAILY => [\n 'fromDate' => $now->copy()->subDay()->startOfDay(),\n 'toDate' => $now->copy()->subDay()->endOfDay(),\n ],\n self::FREQUENCY_WEEKLY => [\n 'fromDate' => $now->copy()->subWeeks(1)->startOfDay(),\n 'toDate' => $now->copy()->subDay()->endOfDay(),\n ],\n self::FREQUENCY_MONTHLY => [\n 'fromDate' => $now->copy()->subMonths(1)->startOfDay(),\n 'toDate' => $now->copy()->subDay()->endOfDay(),\n ],\n self::FREQUENCY_QUARTERLY => [\n 'fromDate' => $now->copy()->subMonths(3)->startOfDay(),\n 'toDate' => $now->copy()->subDay()->endOfDay(),\n ],\n default => throw new InvalidArgumentException(\"Unsupported frequency: {$frequency}\"),\n };\n }\n\n private function formatReportPeriodName(string $frequency, Carbon $from, Carbon $to): string\n {\n $fromYear = $from->format('Y');\n $toYear = $to->format('Y');\n $differentYears = $fromYear !== $toYear;\n\n switch ($frequency) {\n case self::FREQUENCY_DAILY:\n return $from->format('j M Y');\n\n case self::FREQUENCY_QUARTERLY:\n // 'Jan-Mar 2025' or 'Nov 2024-Jan 2025' if years differ\n $startMonth = $from->format('M');\n $endMonth = $to->copy()->subMonth();\n $endMonthName = $endMonth->format('M');\n $endMonthYear = $endMonth->format('Y');\n\n if ($differentYears) {\n return \"{$startMonth} {$fromYear} - {$endMonthName} {$endMonthYear}\";\n }\n\n return \"{$startMonth} - {$endMonthName} {$toYear}\";\n\n case self::FREQUENCY_MONTHLY:\n // 'May 2025' - monthly reports are always within the same year\n return $from->format('M Y');\n\n case self::FREQUENCY_WEEKLY:\n // '4 - 8 Aug 2025', '27 Oct - 3 Nov 2025', or '28 Dec 2024 - 3 Jan 2025' if years differ\n $startDay = $from->format('j');\n $endDay = $to->format('j');\n $startMonth = $from->format('M');\n $endMonth = $to->format('M');\n\n if ($differentYears) {\n return \"{$startDay} {$startMonth} {$fromYear} - {$endDay} {$endMonth} {$toYear}\";\n }\n\n if ($startMonth !== $endMonth) {\n return \"{$startDay} {$startMonth} - {$endDay} {$endMonth} {$toYear}\";\n }\n\n return \"{$startDay} - {$endDay} {$endMonth} {$toYear}\";\n\n case self::FREQUENCY_ONE_OFF:\n // '2 May-31 May 2025' or '15 Dec 2024-15 Jan 2025' if years differ\n $startDay = $from->format('j');\n $startMonth = $from->format('M');\n $endDay = $to->format('j');\n $endMonth = $to->format('M');\n\n // If same month and year, use a format like '2-31 May 2025'\n if ($startMonth === $endMonth && ! $differentYears) {\n return \"{$startDay} - {$endDay} {$startMonth} {$toYear}\";\n }\n\n // If different years, include both years\n if ($differentYears) {\n return \"{$startDay} {$startMonth} {$fromYear} - {$endDay} {$endMonth} {$toYear}\";\n }\n\n // Same year but different months\n return \"{$startDay} {$startMonth} - {$endDay} {$endMonth} {$toYear}\";\n\n default:\n // Default format for unknown frequencies\n return $from->format('j M Y') . ' - ' . $to->format('j M Y');\n }\n }\n\n public function sanitizeFileName(string $fileName): string\n {\n return str_replace(['/', '\\\\'], '-', $fileName);\n }\n\n private function getPayload(AutomatedReportsService $automatedReportsService)\n {\n $reportResult = AutomatedReportResult::find(269);\n $automatedReport = $reportResult->getReport();\n $activityIds = [1,2,3];\n $payload = $automatedReportsService->getAskJiminnyGenerateReportPayload(\n automatedReport: $automatedReport,\n reportResult: $reportResult,\n activityIds: $activityIds,\n );\n\n \\Illuminate\\Support\\Facades\\Log::channel('custom_channel')->info('$payload ' . PHP_EOL . print_r($payload, true));\n }\n\n private function rateLimit()\n {\n $team = Team::find(2);\n $config = $team->getCrmConfiguration();\n\n $crmResolver = app(CrmOwnerResolver::class, [\n 'team' => $team,\n 'integrationAdmin' => $team->getOwner(),\n 'providerSlug' => $config->getProviderName(),\n ]);\n\n $crmService = $crmResolver->prepareCrmService();\n\n for ($i = 0 ; $i < 3; $i++) {\n// if ($i % 25 === 0) {\n// $this->info(\"Syncing opportunity {$i}\");\n $this->info(\"Matching contact {$i}\");\n// }\n// $crmService->syncOpportunity('374720564');\n $crmService->matchByName('Robot');\n }\n }\n\n private function simulateMatchActivityStorm(int $teamId = 2, int $count = 100): void\n {\n $team = Team::find($teamId);\n $config = $team->getCrmConfiguration();\n\n $activities = Activity::query()\n ->where('crm_configuration_id', $config->getId())\n ->orderByDesc('id')\n ->limit($count)\n ->get();\n\n $this->info(\"Dispatching {$activities->count()} MatchActivityCrmData jobs (portal={$config->getId()})\");\n\n foreach ($activities as $activity) {\n MatchActivityCrmData::dispatch($activity->getId(), $config, true);\n }\n\n $this->info('Done. Watch logs and run jiminny:debug observeRateLimit to inspect cache state.');\n }\n\n private function simulateVerifyTaskStorm(int $teamId = 2, int $count = 100): void\n {\n $activities = Activity::query()\n ->where('team_id', $teamId)\n ->whereNotNull('crm_provider_id')\n ->orderByDesc('id')\n ->limit($count)\n ->get();\n\n $this->info(\"Dispatching {$activities->count()} VerifyActivityCrmTaskJob jobs\");\n\n foreach ($activities as $activity) {\n VerifyActivityCrmTaskJob::dispatch($activity->getId());\n }\n\n $this->info('Done.');\n }\n\n private function observeRateLimitCache(int $teamId = 2): void\n {\n $team = Team::find($teamId);\n $config = $team->getCrmConfiguration();\n $key = sprintf('hubspot:ratelimit:portal:%d', $config->getId());\n\n $value = Redis::get($key);\n $ttl = Redis::ttl($key);\n\n $this->info(\"Redis key: {$key}\");\n $this->info('Value: ' . ($value ?? '(empty)'));\n $this->info(\"TTL: {$ttl}s\");\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Show Replace Field","depth":4,"bounds":{"left":0.5575133,"top":0.08060654,"width":0.008643617,"height":0.01915403},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Search History","depth":3,"bounds":{"left":0.57014626,"top":0.07980846,"width":0.00731383,"height":0.017557861},"on_screen":true,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"429","depth":4,"bounds":{"left":0.58111703,"top":0.07980846,"width":0.077792555,"height":0.015961692},"on_screen":true,"value":"429","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"New Line","depth":3,"bounds":{"left":0.66788566,"top":0.07980846,"width":0.00731383,"height":0.017557861},"on_screen":true,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Match Case","depth":3,"bounds":{"left":0.67785907,"top":0.07980846,"width":0.00731383,"height":0.017557861},"on_screen":true,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Words","depth":3,"bounds":{"left":0.68650264,"top":0.07980846,"width":0.00731383,"height":0.017557861},"on_screen":true,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Regex","depth":3,"bounds":{"left":0.69514626,"top":0.07980846,"width":0.00731383,"height":0.017557861},"on_screen":true,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Replace History","depth":3,"bounds":{"left":0.27027926,"top":1.0,"width":0.00731383,"height":0.0},"on_screen":false,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"Replace","depth":4,"on_screen":false,"role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"New Line","depth":3,"bounds":{"left":0.27027926,"top":1.0,"width":0.00731383,"height":0.0},"on_screen":false,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXCheckBox","text":"Preserve case","depth":3,"bounds":{"left":0.27027926,"top":1.0,"width":0.00731383,"height":0.0},"on_screen":false,"role_description":"checkbox","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"2/2","depth":4,"bounds":{"left":0.7087766,"top":0.079010375,"width":0.025598405,"height":0.017557861},"on_screen":true,"role_description":"text"},{"role":"AXButton","text":"Previous Occurrence","depth":4,"bounds":{"left":0.734375,"top":0.07821229,"width":0.008643617,"height":0.01915403},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Occurrence","depth":4,"bounds":{"left":0.7430186,"top":0.07821229,"width":0.008643617,"height":0.01915403},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Filter Search Results","depth":4,"bounds":{"left":0.75166225,"top":0.07821229,"width":0.008643617,"height":0.01915403},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Open in Window, Multiple Cursors","depth":4,"bounds":{"left":0.7603058,"top":0.07821229,"width":0.008643617,"height":0.01915403},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXLink","text":"Click to highlight","depth":4,"on_screen":false,"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close","depth":4,"bounds":{"left":0.97539896,"top":0.07821229,"width":0.008643617,"height":0.01915403},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"on_screen":false,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"174","depth":4,"bounds":{"left":0.96077126,"top":0.10933759,"width":0.011635638,"height":0.015163607},"on_screen":true,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.9740692,"top":0.10774142,"width":0.00731383,"height":0.018355945},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.98138297,"top":0.10774142,"width":0.006981383,"height":0.018355945},"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"[2026-05-11 11:06:08] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":612562,\"team_id\":2,\"email\":\"horen.kirazyan@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"c1949c5e-c0f4-431e-ba26-59d49408d871\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:08] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":612562,\"participants_processed\":2,\"exact_matches\":0,\"domain_matches\":1,\"best_match_found\":true} {\"correlation_id\":\"c1949c5e-c0f4-431e-ba26-59d49408d871\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:08] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612562} {\"correlation_id\":\"c1949c5e-c0f4-431e-ba26-59d49408d871\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:08] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612562,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"c1949c5e-c0f4-431e-ba26-59d49408d871\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:08] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":612562,\"remote_search\":true,\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89} {\"correlation_id\":\"c1949c5e-c0f4-431e-ba26-59d49408d871\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:09] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":612561,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89}} {\"correlation_id\":\"b715f531-04da-410f-be31-055aa1c13754\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:09] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612561} {\"correlation_id\":\"b715f531-04da-410f-be31-055aa1c13754\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:09] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612561,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"b715f531-04da-410f-be31-055aa1c13754\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:09] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":612561,\"participants\":[{\"id\":999780,\"user_id\":206,\"contact_id\":null,\"lead_id\":null},{\"id\":999781,\"user_id\":null,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"b715f531-04da-410f-be31-055aa1c13754\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:09] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"b715f531-04da-410f-be31-055aa1c13754\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:09] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"b715f531-04da-410f-be31-055aa1c13754\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:09] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"b715f531-04da-410f-be31-055aa1c13754\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:09] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"b715f531-04da-410f-be31-055aa1c13754\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:09] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":612561,\"team_id\":2,\"email\":\"horen.kirazyan@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"b715f531-04da-410f-be31-055aa1c13754\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:09] local.INFO: [Prospect match] Cache miss, calling the API {\"identifier_type\":\"email\",\"identifier\":\"447700174614.447782589921.OeREojLVnk@txt.staging.jiminny.com\"} {\"correlation_id\":\"b715f531-04da-410f-be31-055aa1c13754\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:10] local.INFO: [Hubspot] Failed to fetch contact {\"email\":\"447700174614.447782589921.OeREojLVnk@txt.staging.jiminny.com\",\"reason\":\"[404] Client error: `GET https://api.hubapi.com/crm/v3/objects/contacts/447700174614.447782589921.OeREojLVnk%40txt.staging.jiminny.com?properties=email%2Cfirstname%2Clastname%2Ccountry%2Cphone%2Cmobilephone%2Cjobtitle%2Chubspot_owner_id%2Cassociatedcompanyid%2Cphoto&archived=0&idProperty=email` resulted in a `404 Not Found` response\"} {\"correlation_id\":\"b715f531-04da-410f-be31-055aa1c13754\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:10] local.INFO: [Prospect match] API returned empty result, caching the miss with empty prospect data {\"identifier_type\":\"email\",\"identifier\":\"447700174614.447782589921.OeREojLVnk@txt.staging.jiminny.com\"} {\"correlation_id\":\"b715f531-04da-410f-be31-055aa1c13754\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:10] local.INFO: [Prospect match] Resolved company domain from email {\"email\":\"447700174614.447782589921.OeREojLVnk@txt.staging.jiminny.com\",\"domain\":\"jiminny.com\"} {\"correlation_id\":\"b715f531-04da-410f-be31-055aa1c13754\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:10] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":612561,\"participants_processed\":2,\"exact_matches\":0,\"domain_matches\":1,\"best_match_found\":true} {\"correlation_id\":\"b715f531-04da-410f-be31-055aa1c13754\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:10] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612561} {\"correlation_id\":\"b715f531-04da-410f-be31-055aa1c13754\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:10] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612561,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"b715f531-04da-410f-be31-055aa1c13754\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:10] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":612561,\"remote_search\":true,\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89} {\"correlation_id\":\"b715f531-04da-410f-be31-055aa1c13754\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:10] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":612560,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89}} {\"correlation_id\":\"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:10] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612560} {\"correlation_id\":\"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:10] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612560,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:10] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":612560,\"participants\":[{\"id\":999778,\"user_id\":null,\"contact_id\":null,\"lead_id\":null},{\"id\":999779,\"user_id\":206,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"447782589921@txt.staging.jiminny.com\"} {\"correlation_id\":\"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [Prospect match] cached empty result - no API calls, try next matching method {\"identifier_type\":\"email\",\"identifier\":\"447782589921@txt.staging.jiminny.com\"} {\"correlation_id\":\"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [Prospect match] Resolved company domain from email {\"email\":\"447782589921@txt.staging.jiminny.com\",\"domain\":\"jiminny.com\"} {\"correlation_id\":\"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":612560,\"team_id\":2,\"email\":\"horen.kirazyan@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":612560,\"participants_processed\":2,\"exact_matches\":0,\"domain_matches\":1,\"best_match_found\":true} {\"correlation_id\":\"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612560} {\"correlation_id\":\"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [ EsUpdateProcessManager ] Finished updating entities in ES {\"worker\":\"\",\"peak_memory\":\"99.73 MB\",\"elapsed_seconds\":0.74,\"update_target\":\"activities\",\"should_iterate_again\":false} {\"correlation_id\":\"38935ae1-fcb3-437e-9c9e-1b7415a0b7e1\",\"trace_id\":\"325a2f9b-9c66-4223-ac02-1a43723f4315\"}\n[2026-05-11 11:06:11] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612560,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":612560,\"remote_search\":true,\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89} {\"correlation_id\":\"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":612559,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":97,\"account_id\":69,\"opportunity_id\":5011,\"stage_id\":34}} {\"correlation_id\":\"4d59a59c-38a5-4a1d-9379-6ceaf67b747e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612559} {\"correlation_id\":\"4d59a59c-38a5-4a1d-9379-6ceaf67b747e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612559,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"4d59a59c-38a5-4a1d-9379-6ceaf67b747e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":612559,\"participants\":[{\"id\":999776,\"user_id\":206,\"contact_id\":null,\"lead_id\":null},{\"id\":999777,\"user_id\":null,\"contact_id\":97,\"lead_id\":null}]} {\"correlation_id\":\"4d59a59c-38a5-4a1d-9379-6ceaf67b747e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"4d59a59c-38a5-4a1d-9379-6ceaf67b747e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"4d59a59c-38a5-4a1d-9379-6ceaf67b747e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"4d59a59c-38a5-4a1d-9379-6ceaf67b747e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"4d59a59c-38a5-4a1d-9379-6ceaf67b747e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":612559,\"team_id\":2,\"email\":\"horen.kirazyan@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"4d59a59c-38a5-4a1d-9379-6ceaf67b747e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: ProspectCache - Searching DB for opportunity by owner {\"account_id\":69,\"contact_id\":97,\"owner_id\":206} {\"correlation_id\":\"4d59a59c-38a5-4a1d-9379-6ceaf67b747e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: ProspectCache - Fallback DB opportunity search {\"account_id\":69,\"contact_id\":97} {\"correlation_id\":\"4d59a59c-38a5-4a1d-9379-6ceaf67b747e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: ProspectCache - Opportunity DB search results {\"account_id\":69,\"contact_id\":97,\"opportunity_id\":5011} {\"correlation_id\":\"4d59a59c-38a5-4a1d-9379-6ceaf67b747e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"robinsoncrusoe@test.com\"} {\"correlation_id\":\"4d59a59c-38a5-4a1d-9379-6ceaf67b747e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":612559,\"participants_processed\":2,\"exact_matches\":1,\"domain_matches\":0,\"best_match_found\":true} {\"correlation_id\":\"4d59a59c-38a5-4a1d-9379-6ceaf67b747e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612559} {\"correlation_id\":\"4d59a59c-38a5-4a1d-9379-6ceaf67b747e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612559,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"4d59a59c-38a5-4a1d-9379-6ceaf67b747e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":612559,\"remote_search\":true,\"lead_id\":null,\"contact_id\":97,\"account_id\":69,\"opportunity_id\":5011,\"stage_id\":34} {\"correlation_id\":\"4d59a59c-38a5-4a1d-9379-6ceaf67b747e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:12] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":612558,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":97,\"account_id\":69,\"opportunity_id\":5011,\"stage_id\":34}} {\"correlation_id\":\"a105800d-d2f7-4772-a534-7791e262461c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:12] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612558} {\"correlation_id\":\"a105800d-d2f7-4772-a534-7791e262461c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:12] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612558,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"a105800d-d2f7-4772-a534-7791e262461c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:12] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":612558,\"participants\":[{\"id\":999774,\"user_id\":206,\"contact_id\":null,\"lead_id\":null},{\"id\":999775,\"user_id\":null,\"contact_id\":97,\"lead_id\":null}]} {\"correlation_id\":\"a105800d-d2f7-4772-a534-7791e262461c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:12] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"a105800d-d2f7-4772-a534-7791e262461c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:12] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"a105800d-d2f7-4772-a534-7791e262461c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:12] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"a105800d-d2f7-4772-a534-7791e262461c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:12] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"a105800d-d2f7-4772-a534-7791e262461c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:12] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":612558,\"team_id\":2,\"email\":\"horen.kirazyan@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"a105800d-d2f7-4772-a534-7791e262461c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:12] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"robinsoncrusoe@test.com\"} {\"correlation_id\":\"a105800d-d2f7-4772-a534-7791e262461c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:12] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":612558,\"participants_processed\":2,\"exact_matches\":1,\"domain_matches\":0,\"best_match_found\":true} {\"correlation_id\":\"a105800d-d2f7-4772-a534-7791e262461c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:12] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612558} {\"correlation_id\":\"a105800d-d2f7-4772-a534-7791e262461c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:12] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612558,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"a105800d-d2f7-4772-a534-7791e262461c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:12] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":612558,\"remote_search\":true,\"lead_id\":null,\"contact_id\":97,\"account_id\":69,\"opportunity_id\":5011,\"stage_id\":34} {\"correlation_id\":\"a105800d-d2f7-4772-a534-7791e262461c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:13] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":612557,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":97,\"account_id\":69,\"opportunity_id\":5011,\"stage_id\":34}} {\"correlation_id\":\"3ec57437-5796-4e44-8bf0-aaec149ac813\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:13] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612557} {\"correlation_id\":\"3ec57437-5796-4e44-8bf0-aaec149ac813\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:13] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612557,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"3ec57437-5796-4e44-8bf0-aaec149ac813\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:13] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":612557,\"participants\":[{\"id\":999772,\"user_id\":206,\"contact_id\":null,\"lead_id\":null},{\"id\":999773,\"user_id\":null,\"contact_id\":97,\"lead_id\":null}]} {\"correlation_id\":\"3ec57437-5796-4e44-8bf0-aaec149ac813\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:13] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"mailbox:skip-lists:refresh\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.727} {\"correlation_id\":\"5203040f-23be-43e6-8410-ba49ee1b38b4\",\"trace_id\":\"e787953d-9b78-4b63-a987-4f93f6892984\"}\n[2026-05-11 11:06:13] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"3ec57437-5796-4e44-8bf0-aaec149ac813\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:13] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"3ec57437-5796-4e44-8bf0-aaec149ac813\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:13] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"3ec57437-5796-4e44-8bf0-aaec149ac813\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:13] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"3ec57437-5796-4e44-8bf0-aaec149ac813\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:13] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":612557,\"team_id\":2,\"email\":\"horen.kirazyan@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"3ec57437-5796-4e44-8bf0-aaec149ac813\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:13] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"robinsoncrusoe@test.com\"} {\"correlation_id\":\"3ec57437-5796-4e44-8bf0-aaec149ac813\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:13] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":612557,\"participants_processed\":2,\"exact_matches\":1,\"domain_matches\":0,\"best_match_found\":true} {\"correlation_id\":\"3ec57437-5796-4e44-8bf0-aaec149ac813\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:13] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612557} {\"correlation_id\":\"3ec57437-5796-4e44-8bf0-aaec149ac813\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:13] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612557,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"3ec57437-5796-4e44-8bf0-aaec149ac813\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:13] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":612557,\"remote_search\":true,\"lead_id\":null,\"contact_id\":97,\"account_id\":69,\"opportunity_id\":5011,\"stage_id\":34} {\"correlation_id\":\"3ec57437-5796-4e44-8bf0-aaec149ac813\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:13] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage for command {\"command\":\"mailbox:skip-lists:refresh\",\"memoryBeforeCommandInMb\":60.0,\"memoryAfterCommandInMB\":60.0,\"memoryPeakBeforeCommandInMb\":99.727,\"memoryPeakAfterCommandInMB\":99.727} {\"correlation_id\":\"5203040f-23be-43e6-8410-ba49ee1b38b4\",\"trace_id\":\"e787953d-9b78-4b63-a987-4f93f6892984\"}\n[2026-05-11 11:06:13] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":612556,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":97,\"account_id\":69,\"opportunity_id\":5011,\"stage_id\":34}} {\"correlation_id\":\"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:13] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612556} {\"correlation_id\":\"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:13] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612556,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:13] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":612556,\"participants\":[{\"id\":999770,\"user_id\":206,\"contact_id\":null,\"lead_id\":null},{\"id\":999771,\"user_id\":null,\"contact_id\":97,\"lead_id\":null}]} {\"correlation_id\":\"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:13] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:13] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:13] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:13] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":612556,\"team_id\":2,\"email\":\"horen.kirazyan@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"robinsoncrusoe@test.com\"} {\"correlation_id\":\"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":612556,\"participants_processed\":2,\"exact_matches\":1,\"domain_matches\":0,\"best_match_found\":true} {\"correlation_id\":\"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612556} {\"correlation_id\":\"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612556,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":612556,\"remote_search\":true,\"lead_id\":null,\"contact_id\":97,\"account_id\":69,\"opportunity_id\":5011,\"stage_id\":34} {\"correlation_id\":\"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":612555,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":97,\"account_id\":69,\"opportunity_id\":5011,\"stage_id\":34}} {\"correlation_id\":\"3e9d1a70-5491-44ae-8f28-5cb9b60e1859\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612555} {\"correlation_id\":\"3e9d1a70-5491-44ae-8f28-5cb9b60e1859\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612555,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"3e9d1a70-5491-44ae-8f28-5cb9b60e1859\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":612555,\"participants\":[{\"id\":999768,\"user_id\":206,\"contact_id\":null,\"lead_id\":null},{\"id\":999769,\"user_id\":null,\"contact_id\":97,\"lead_id\":null}]} {\"correlation_id\":\"3e9d1a70-5491-44ae-8f28-5cb9b60e1859\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"3e9d1a70-5491-44ae-8f28-5cb9b60e1859\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"3e9d1a70-5491-44ae-8f28-5cb9b60e1859\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"3e9d1a70-5491-44ae-8f28-5cb9b60e1859\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"3e9d1a70-5491-44ae-8f28-5cb9b60e1859\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":612555,\"team_id\":2,\"email\":\"horen.kirazyan@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"3e9d1a70-5491-44ae-8f28-5cb9b60e1859\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"robinsoncrusoe@test.com\"} {\"correlation_id\":\"3e9d1a70-5491-44ae-8f28-5cb9b60e1859\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":612555,\"participants_processed\":2,\"exact_matches\":1,\"domain_matches\":0,\"best_match_found\":true} {\"correlation_id\":\"3e9d1a70-5491-44ae-8f28-5cb9b60e1859\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612555} {\"correlation_id\":\"3e9d1a70-5491-44ae-8f28-5cb9b60e1859\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612555,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"3e9d1a70-5491-44ae-8f28-5cb9b60e1859\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":612555,\"remote_search\":true,\"lead_id\":null,\"contact_id\":97,\"account_id\":69,\"opportunity_id\":5011,\"stage_id\":34} {\"correlation_id\":\"3e9d1a70-5491-44ae-8f28-5cb9b60e1859\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":612554,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":97,\"account_id\":69,\"opportunity_id\":5011,\"stage_id\":34}} {\"correlation_id\":\"7101e979-3a7e-4b2c-bf04-dc6a24ba1356\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612554} {\"correlation_id\":\"7101e979-3a7e-4b2c-bf04-dc6a24ba1356\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612554,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"7101e979-3a7e-4b2c-bf04-dc6a24ba1356\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":612554,\"participants\":[{\"id\":999766,\"user_id\":206,\"contact_id\":null,\"lead_id\":null},{\"id\":999767,\"user_id\":null,\"contact_id\":97,\"lead_id\":null}]} {\"correlation_id\":\"7101e979-3a7e-4b2c-bf04-dc6a24ba1356\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"7101e979-3a7e-4b2c-bf04-dc6a24ba1356\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"7101e979-3a7e-4b2c-bf04-dc6a24ba1356\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"7101e979-3a7e-4b2c-bf04-dc6a24ba1356\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"7101e979-3a7e-4b2c-bf04-dc6a24ba1356\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":612554,\"team_id\":2,\"email\":\"horen.kirazyan@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"7101e979-3a7e-4b2c-bf04-dc6a24ba1356\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"robinsoncrusoe@test.com\"} {\"correlation_id\":\"7101e979-3a7e-4b2c-bf04-dc6a24ba1356\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":612554,\"participants_processed\":2,\"exact_matches\":1,\"domain_matches\":0,\"best_match_found\":true} {\"correlation_id\":\"7101e979-3a7e-4b2c-bf04-dc6a24ba1356\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612554} {\"correlation_id\":\"7101e979-3a7e-4b2c-bf04-dc6a24ba1356\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612554,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"7101e979-3a7e-4b2c-bf04-dc6a24ba1356\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":612554,\"remote_search\":true,\"lead_id\":null,\"contact_id\":97,\"account_id\":69,\"opportunity_id\":5011,\"stage_id\":34} {\"correlation_id\":\"7101e979-3a7e-4b2c-bf04-dc6a24ba1356\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":612553,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":97,\"account_id\":69,\"opportunity_id\":5011,\"stage_id\":34}} {\"correlation_id\":\"346dc229-757b-496c-aceb-d2ddb367c7d6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612553} {\"correlation_id\":\"346dc229-757b-496c-aceb-d2ddb367c7d6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612553,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"346dc229-757b-496c-aceb-d2ddb367c7d6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":612553,\"participants\":[{\"id\":999764,\"user_id\":206,\"contact_id\":null,\"lead_id\":null},{\"id\":999765,\"user_id\":null,\"contact_id\":97,\"lead_id\":null}]} {\"correlation_id\":\"346dc229-757b-496c-aceb-d2ddb367c7d6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"346dc229-757b-496c-aceb-d2ddb367c7d6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"346dc229-757b-496c-aceb-d2ddb367c7d6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"346dc229-757b-496c-aceb-d2ddb367c7d6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"346dc229-757b-496c-aceb-d2ddb367c7d6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":612553,\"team_id\":2,\"email\":\"horen.kirazyan@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"346dc229-757b-496c-aceb-d2ddb367c7d6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"robinsoncrusoe@test.com\"} {\"correlation_id\":\"346dc229-757b-496c-aceb-d2ddb367c7d6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":612553,\"participants_processed\":2,\"exact_matches\":1,\"domain_matches\":0,\"best_match_found\":true} {\"correlation_id\":\"346dc229-757b-496c-aceb-d2ddb367c7d6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612553} {\"correlation_id\":\"346dc229-757b-496c-aceb-d2ddb367c7d6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612553,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"346dc229-757b-496c-aceb-d2ddb367c7d6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":612553,\"remote_search\":true,\"lead_id\":null,\"contact_id\":97,\"account_id\":69,\"opportunity_id\":5011,\"stage_id\":34} {\"correlation_id\":\"346dc229-757b-496c-aceb-d2ddb367c7d6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":612552,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":97,\"account_id\":69,\"opportunity_id\":5011,\"stage_id\":34}} {\"correlation_id\":\"56ef8656-0bfa-4f11-a245-9254b8179555\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612552} {\"correlation_id\":\"56ef8656-0bfa-4f11-a245-9254b8179555\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612552,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"56ef8656-0bfa-4f11-a245-9254b8179555\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":612552,\"participants\":[{\"id\":999762,\"user_id\":206,\"contact_id\":null,\"lead_id\":null},{\"id\":999763,\"user_id\":null,\"contact_id\":97,\"lead_id\":null}]} {\"correlation_id\":\"56ef8656-0bfa-4f11-a245-9254b8179555\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"56ef8656-0bfa-4f11-a245-9254b8179555\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"56ef8656-0bfa-4f11-a245-9254b8179555\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"56ef8656-0bfa-4f11-a245-9254b8179555\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"56ef8656-0bfa-4f11-a245-9254b8179555\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":612552,\"team_id\":2,\"email\":\"horen.kirazyan@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"56ef8656-0bfa-4f11-a245-9254b8179555\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"robinsoncrusoe@test.com\"} {\"correlation_id\":\"56ef8656-0bfa-4f11-a245-9254b8179555\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":612552,\"participants_processed\":2,\"exact_matches\":1,\"domain_matches\":0,\"best_match_found\":true} {\"correlation_id\":\"56ef8656-0bfa-4f11-a245-9254b8179555\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612552} {\"correlation_id\":\"56ef8656-0bfa-4f11-a245-9254b8179555\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612552,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"56ef8656-0bfa-4f11-a245-9254b8179555\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":612552,\"remote_search\":true,\"lead_id\":null,\"contact_id\":97,\"account_id\":69,\"opportunity_id\":5011,\"stage_id\":34} {\"correlation_id\":\"56ef8656-0bfa-4f11-a245-9254b8179555\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":612551,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":97,\"account_id\":69,\"opportunity_id\":5011,\"stage_id\":34}} {\"correlation_id\":\"df5e35c3-b4ae-4eba-9108-cd30ac152335\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612551} {\"correlation_id\":\"df5e35c3-b4ae-4eba-9108-cd30ac152335\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612551,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"df5e35c3-b4ae-4eba-9108-cd30ac152335\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":612551,\"participants\":[{\"id\":999760,\"user_id\":206,\"contact_id\":null,\"lead_id\":null},{\"id\":999761,\"user_id\":null,\"contact_id\":97,\"lead_id\":null}]} {\"correlation_id\":\"df5e35c3-b4ae-4eba-9108-cd30ac152335\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"df5e35c3-b4ae-4eba-9108-cd30ac152335\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"df5e35c3-b4ae-4eba-9108-cd30ac152335\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"df5e35c3-b4ae-4eba-9108-cd30ac152335\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"df5e35c3-b4ae-4eba-9108-cd30ac152335\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":612551,\"team_id\":2,\"email\":\"horen.kirazyan@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"df5e35c3-b4ae-4eba-9108-cd30ac152335\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"robinsoncrusoe@test.com\"} {\"correlation_id\":\"df5e35c3-b4ae-4eba-9108-cd30ac152335\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":612551,\"participants_processed\":2,\"exact_matches\":1,\"domain_matches\":0,\"best_match_found\":true} {\"correlation_id\":\"df5e35c3-b4ae-4eba-9108-cd30ac152335\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612551} {\"correlation_id\":\"df5e35c3-b4ae-4eba-9108-cd30ac152335\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612551,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"df5e35c3-b4ae-4eba-9108-cd30ac152335\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":612551,\"remote_search\":true,\"lead_id\":null,\"contact_id\":97,\"account_id\":69,\"opportunity_id\":5011,\"stage_id\":34} {\"correlation_id\":\"df5e35c3-b4ae-4eba-9108-cd30ac152335\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":612550,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":97,\"account_id\":69,\"opportunity_id\":5011,\"stage_id\":34}} {\"correlation_id\":\"64768905-983e-4f4e-a368-901931486f34\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612550} {\"correlation_id\":\"64768905-983e-4f4e-a368-901931486f34\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612550,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"64768905-983e-4f4e-a368-901931486f34\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":612550,\"participants\":[{\"id\":999758,\"user_id\":206,\"contact_id\":null,\"lead_id\":null},{\"id\":999759,\"user_id\":null,\"contact_id\":97,\"lead_id\":null}]} {\"correlation_id\":\"64768905-983e-4f4e-a368-901931486f34\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"64768905-983e-4f4e-a368-901931486f34\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"64768905-983e-4f4e-a368-901931486f34\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"64768905-983e-4f4e-a368-901931486f34\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"64768905-983e-4f4e-a368-901931486f34\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":612550,\"team_id\":2,\"email\":\"horen.kirazyan@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"64768905-983e-4f4e-a368-901931486f34\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"robinsoncrusoe@test.com\"} {\"correlation_id\":\"64768905-983e-4f4e-a368-901931486f34\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":612550,\"participants_processed\":2,\"exact_matches\":1,\"domain_matches\":0,\"best_match_found\":true} {\"correlation_id\":\"64768905-983e-4f4e-a368-901931486f34\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612550} {\"correlation_id\":\"64768905-983e-4f4e-a368-901931486f34\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612550,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"64768905-983e-4f4e-a368-901931486f34\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":612550,\"remote_search\":true,\"lead_id\":null,\"contact_id\":97,\"account_id\":69,\"opportunity_id\":5011,\"stage_id\":34} {\"correlation_id\":\"64768905-983e-4f4e-a368-901931486f34\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":612549,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":97,\"account_id\":69,\"opportunity_id\":5011,\"stage_id\":34}} {\"correlation_id\":\"97c9609f-bef0-4e95-8301-9f5549d29c1e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612549} {\"correlation_id\":\"97c9609f-bef0-4e95-8301-9f5549d29c1e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612549,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"97c9609f-bef0-4e95-8301-9f5549d29c1e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":612549,\"participants\":[{\"id\":999756,\"user_id\":206,\"contact_id\":null,\"lead_id\":null},{\"id\":999757,\"user_id\":null,\"contact_id\":97,\"lead_id\":null}]} {\"correlation_id\":\"97c9609f-bef0-4e95-8301-9f5549d29c1e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"97c9609f-bef0-4e95-8301-9f5549d29c1e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"97c9609f-bef0-4e95-8301-9f5549d29c1e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"97c9609f-bef0-4e95-8301-9f5549d29c1e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"97c9609f-bef0-4e95-8301-9f5549d29c1e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":612549,\"team_id\":2,\"email\":\"horen.kirazyan@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"97c9609f-bef0-4e95-8301-9f5549d29c1e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"robinsoncrusoe@test.com\"} {\"correlation_id\":\"97c9609f-bef0-4e95-8301-9f5549d29c1e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":612549,\"participants_processed\":2,\"exact_matches\":1,\"domain_matches\":0,\"best_match_found\":true} {\"correlation_id\":\"97c9609f-bef0-4e95-8301-9f5549d29c1e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612549} {\"correlation_id\":\"97c9609f-bef0-4e95-8301-9f5549d29c1e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612549,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"97c9609f-bef0-4e95-8301-9f5549d29c1e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":612549,\"remote_search\":true,\"lead_id\":null,\"contact_id\":97,\"account_id\":69,\"opportunity_id\":5011,\"stage_id\":34} {\"correlation_id\":\"97c9609f-bef0-4e95-8301-9f5549d29c1e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":612365,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":4491,\"account_id\":243,\"opportunity_id\":276,\"stage_id\":36}} {\"correlation_id\":\"7761b4e0-c2d4-4805-8d1d-26728cbffb07\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612365} {\"correlation_id\":\"7761b4e0-c2d4-4805-8d1d-26728cbffb07\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612365,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"7761b4e0-c2d4-4805-8d1d-26728cbffb07\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":612365,\"participants\":[{\"id\":999563,\"user_id\":null,\"contact_id\":4491,\"lead_id\":null},{\"id\":999564,\"user_id\":206,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"7761b4e0-c2d4-4805-8d1d-26728cbffb07\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"7761b4e0-c2d4-4805-8d1d-26728cbffb07\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"7761b4e0-c2d4-4805-8d1d-26728cbffb07\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"7761b4e0-c2d4-4805-8d1d-26728cbffb07\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"7761b4e0-c2d4-4805-8d1d-26728cbffb07\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"horencho@gmail.com\"} {\"correlation_id\":\"7761b4e0-c2d4-4805-8d1d-26728cbffb07\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":612365,\"team_id\":2,\"email\":\"horen.kirazyan@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"7761b4e0-c2d4-4805-8d1d-26728cbffb07\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":612365,\"participants_processed\":2,\"exact_matches\":1,\"domain_matches\":0,\"best_match_found\":true} {\"correlation_id\":\"7761b4e0-c2d4-4805-8d1d-26728cbffb07\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612365} {\"correlation_id\":\"7761b4e0-c2d4-4805-8d1d-26728cbffb07\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612365,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"7761b4e0-c2d4-4805-8d1d-26728cbffb07\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":612365,\"remote_search\":true,\"lead_id\":null,\"contact_id\":4491,\"account_id\":243,\"opportunity_id\":276,\"stage_id\":36} {\"correlation_id\":\"7761b4e0-c2d4-4805-8d1d-26728cbffb07\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":612360,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":4491,\"account_id\":243,\"opportunity_id\":276,\"stage_id\":36}} {\"correlation_id\":\"b8b554cd-4830-4ac5-be75-595cc7eb6ca8\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612360} {\"correlation_id\":\"b8b554cd-4830-4ac5-be75-595cc7eb6ca8\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612360,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"b8b554cd-4830-4ac5-be75-595cc7eb6ca8\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":612360,\"participants\":[{\"id\":999552,\"user_id\":null,\"contact_id\":4491,\"lead_id\":null},{\"id\":999553,\"user_id\":206,\"contact_id\":null,\"lead_id\":null},{\"id\":999565,\"user_id\":null,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"b8b554cd-4830-4ac5-be75-595cc7eb6ca8\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"b8b554cd-4830-4ac5-be75-595cc7eb6ca8\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"b8b554cd-4830-4ac5-be75-595cc7eb6ca8\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"b8b554cd-4830-4ac5-be75-595cc7eb6ca8\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"b8b554cd-4830-4ac5-be75-595cc7eb6ca8\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"horencho@gmail.com\"} {\"correlation_id\":\"b8b554cd-4830-4ac5-be75-595cc7eb6ca8\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":612360,\"team_id\":2,\"email\":\"horen.kirazyan@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"b8b554cd-4830-4ac5-be75-595cc7eb6ca8\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [Hubspot] Pagination completed {\"team_id\":2,\"endpoint\":\"https://api.hubapi.com/crm/v3/objects/contact/search\",\"total_requests\":1,\"total_records_fetched\":0,\"total_elapsed_seconds\":0.31,\"average_seconds_per_request\":0.31} {\"correlation_id\":\"b8b554cd-4830-4ac5-be75-595cc7eb6ca8\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":612360,\"participants_processed\":3,\"exact_matches\":1,\"domain_matches\":0,\"best_match_found\":true} {\"correlation_id\":\"b8b554cd-4830-4ac5-be75-595cc7eb6ca8\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612360} {\"correlation_id\":\"b8b554cd-4830-4ac5-be75-595cc7eb6ca8\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612360,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"b8b554cd-4830-4ac5-be75-595cc7eb6ca8\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":612360,\"remote_search\":true,\"lead_id\":null,\"contact_id\":4491,\"account_id\":243,\"opportunity_id\":276,\"stage_id\":36} {\"correlation_id\":\"b8b554cd-4830-4ac5-be75-595cc7eb6ca8\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":612340,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":4491,\"account_id\":243,\"opportunity_id\":276,\"stage_id\":36}} {\"correlation_id\":\"9a47ab79-dbb3-46e2-ad27-3a5120c080ef\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612340} {\"correlation_id\":\"9a47ab79-dbb3-46e2-ad27-3a5120c080ef\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612340,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"9a47ab79-dbb3-46e2-ad27-3a5120c080ef\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":612340,\"participants\":[{\"id\":999516,\"user_id\":null,\"contact_id\":4491,\"lead_id\":null},{\"id\":999517,\"user_id\":206,\"contact_id\":null,\"lead_id\":null},{\"id\":999518,\"user_id\":null,\"contact_id\":null,\"lead_id\":null},{\"id\":999519,\"user_id\":null,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"9a47ab79-dbb3-46e2-ad27-3a5120c080ef\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"9a47ab79-dbb3-46e2-ad27-3a5120c080ef\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"9a47ab79-dbb3-46e2-ad27-3a5120c080ef\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"9a47ab79-dbb3-46e2-ad27-3a5120c080ef\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"9a47ab79-dbb3-46e2-ad27-3a5120c080ef\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"horencho@gmail.com\"} {\"correlation_id\":\"9a47ab79-dbb3-46e2-ad27-3a5120c080ef\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":612340,\"team_id\":2,\"email\":\"horen.kirazyan@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"9a47ab79-dbb3-46e2-ad27-3a5120c080ef\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [Hubspot] Pagination completed {\"team_id\":2,\"endpoint\":\"https://api.hubapi.com/crm/v3/objects/contact/search\",\"total_requests\":1,\"total_records_fetched\":0,\"total_elapsed_seconds\":0.24,\"average_seconds_per_request\":0.24} {\"correlation_id\":\"9a47ab79-dbb3-46e2-ad27-3a5120c080ef\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":612340,\"participants_processed\":4,\"exact_matches\":1,\"domain_matches\":0,\"best_match_found\":true} {\"correlation_id\":\"9a47ab79-dbb3-46e2-ad27-3a5120c080ef\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [ EsUpdateProcessManager ] Finished updating entities in ES {\"worker\":\"\",\"peak_memory\":\"99.73 MB\",\"elapsed_seconds\":0.45,\"update_target\":\"activities\",\"should_iterate_again\":false} {\"correlation_id\":\"38935ae1-fcb3-437e-9c9e-1b7415a0b7e1\",\"trace_id\":\"325a2f9b-9c66-4223-ac02-1a43723f4315\"}\n[2026-05-11 11:06:16] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612340} {\"correlation_id\":\"9a47ab79-dbb3-46e2-ad27-3a5120c080ef\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612340,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"9a47ab79-dbb3-46e2-ad27-3a5120c080ef\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":612340,\"remote_search\":true,\"lead_id\":null,\"contact_id\":4491,\"account_id\":243,\"opportunity_id\":276,\"stage_id\":36} {\"correlation_id\":\"9a47ab79-dbb3-46e2-ad27-3a5120c080ef\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":612339,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":4491,\"account_id\":243,\"opportunity_id\":276,\"stage_id\":36}} {\"correlation_id\":\"eae16400-35f9-4885-b5fc-edb8872350d4\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612339} {\"correlation_id\":\"eae16400-35f9-4885-b5fc-edb8872350d4\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612339,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"eae16400-35f9-4885-b5fc-edb8872350d4\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":612339,\"participants\":[{\"id\":999514,\"user_id\":null,\"contact_id\":4491,\"lead_id\":null},{\"id\":999515,\"user_id\":206,\"contact_id\":null,\"lead_id\":null},{\"id\":999540,\"user_id\":null,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"eae16400-35f9-4885-b5fc-edb8872350d4\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"eae16400-35f9-4885-b5fc-edb8872350d4\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"eae16400-35f9-4885-b5fc-edb8872350d4\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"eae16400-35f9-4885-b5fc-edb8872350d4\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"eae16400-35f9-4885-b5fc-edb8872350d4\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"horencho@gmail.com\"} {\"correlation_id\":\"eae16400-35f9-4885-b5fc-edb8872350d4\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":612339,\"team_id\":2,\"email\":\"horen.kirazyan@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"eae16400-35f9-4885-b5fc-edb8872350d4\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":612339,\"participants_processed\":3,\"exact_matches\":1,\"domain_matches\":0,\"best_match_found\":true} {\"correlation_id\":\"eae16400-35f9-4885-b5fc-edb8872350d4\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612339} {\"correlation_id\":\"eae16400-35f9-4885-b5fc-edb8872350d4\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612339,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"eae16400-35f9-4885-b5fc-edb8872350d4\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":612339,\"remote_search\":true,\"lead_id\":null,\"contact_id\":4491,\"account_id\":243,\"opportunity_id\":276,\"stage_id\":36} {\"correlation_id\":\"eae16400-35f9-4885-b5fc-edb8872350d4\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":612336,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":4491,\"account_id\":243,\"opportunity_id\":276,\"stage_id\":36}} {\"correlation_id\":\"1028998b-3369-4f2f-b7cb-e4ef79db71a5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612336} {\"correlation_id\":\"1028998b-3369-4f2f-b7cb-e4ef79db71a5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612336,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"1028998b-3369-4f2f-b7cb-e4ef79db71a5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":612336,\"participants\":[{\"id\":999508,\"user_id\":null,\"contact_id\":4491,\"lead_id\":null},{\"id\":999509,\"user_id\":206,\"contact_id\":null,\"lead_id\":null},{\"id\":999512,\"user_id\":null,\"contact_id\":null,\"lead_id\":null},{\"id\":999513,\"user_id\":null,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"1028998b-3369-4f2f-b7cb-e4ef79db71a5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"1028998b-3369-4f2f-b7cb-e4ef79db71a5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"1028998b-3369-4f2f-b7cb-e4ef79db71a5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"1028998b-3369-4f2f-b7cb-e4ef79db71a5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"1028998b-3369-4f2f-b7cb-e4ef79db71a5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"horencho@gmail.com\"} {\"correlation_id\":\"1028998b-3369-4f2f-b7cb-e4ef79db71a5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":612336,\"team_id\":2,\"email\":\"horen.kirazyan@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"1028998b-3369-4f2f-b7cb-e4ef79db71a5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":612336,\"participants_processed\":4,\"exact_matches\":1,\"domain_matches\":0,\"best_match_found\":true} {\"correlation_id\":\"1028998b-3369-4f2f-b7cb-e4ef79db71a5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612336} {\"correlation_id\":\"1028998b-3369-4f2f-b7cb-e4ef79db71a5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612336,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"1028998b-3369-4f2f-b7cb-e4ef79db71a5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":612336,\"remote_search\":true,\"lead_id\":null,\"contact_id\":4491,\"account_id\":243,\"opportunity_id\":276,\"stage_id\":36} {\"correlation_id\":\"1028998b-3369-4f2f-b7cb-e4ef79db71a5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":612183,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":4487,\"account_id\":244,\"opportunity_id\":299,\"stage_id\":36}} {\"correlation_id\":\"135ad495-9ac2-4ae2-9428-66f40f3a4731\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612183} {\"correlation_id\":\"135ad495-9ac2-4ae2-9428-66f40f3a4731\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612183,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"135ad495-9ac2-4ae2-9428-66f40f3a4731\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":612183,\"participants\":[{\"id\":999227,\"user_id\":null,\"contact_id\":4487,\"lead_id\":null},{\"id\":999228,\"user_id\":261,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"135ad495-9ac2-4ae2-9428-66f40f3a4731\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"135ad495-9ac2-4ae2-9428-66f40f3a4731\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"135ad495-9ac2-4ae2-9428-66f40f3a4731\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"135ad495-9ac2-4ae2-9428-66f40f3a4731\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"135ad495-9ac2-4ae2-9428-66f40f3a4731\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: ProspectCache - Searching DB for opportunity by owner {\"account_id\":244,\"contact_id\":4487,\"owner_id\":261} {\"correlation_id\":\"135ad495-9ac2-4ae2-9428-66f40f3a4731\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: ProspectCache - Opportunity DB search results {\"account_id\":244,\"contact_id\":4487,\"opportunity_id\":299} {\"correlation_id\":\"135ad495-9ac2-4ae2-9428-66f40f3a4731\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"robinson@crusoe.com\"} {\"correlation_id\":\"135ad495-9ac2-4ae2-9428-66f40f3a4731\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":612183,\"team_id\":2,\"email\":\"adelina.petrova@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"135ad495-9ac2-4ae2-9428-66f40f3a4731\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":612183,\"participants_processed\":2,\"exact_matches\":1,\"domain_matches\":0,\"best_match_found\":true} {\"correlation_id\":\"135ad495-9ac2-4ae2-9428-66f40f3a4731\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612183} {\"correlation_id\":\"135ad495-9ac2-4ae2-9428-66f40f3a4731\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612183,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"135ad495-9ac2-4ae2-9428-66f40f3a4731\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":612183,\"remote_search\":true,\"lead_id\":null,\"contact_id\":4487,\"account_id\":244,\"opportunity_id\":299,\"stage_id\":36} {\"correlation_id\":\"135ad495-9ac2-4ae2-9428-66f40f3a4731\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":612182,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":4487,\"account_id\":244,\"opportunity_id\":299,\"stage_id\":36}} {\"correlation_id\":\"9a2d51b0-64bc-41ea-86b1-8f3bcbb65a89\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612182} {\"correlation_id\":\"9a2d51b0-64bc-41ea-86b1-8f3bcbb65a89\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612182,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"9a2d51b0-64bc-41ea-86b1-8f3bcbb65a89\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":612182,\"participants\":[{\"id\":999225,\"user_id\":null,\"contact_id\":4487,\"lead_id\":null},{\"id\":999226,\"user_id\":261,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"9a2d51b0-64bc-41ea-86b1-8f3bcbb65a89\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"mailbox:batch:process\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.727} {\"correlation_id\":\"dceccba8-9e9c-476e-993d-809067884ebe\",\"trace_id\":\"66f8bba8-1712-464b-b25a-418f16736be2\"}\n[2026-05-11 11:06:17] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"9a2d51b0-64bc-41ea-86b1-8f3bcbb65a89\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"9a2d51b0-64bc-41ea-86b1-8f3bcbb65a89\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"9a2d51b0-64bc-41ea-86b1-8f3bcbb65a89\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"9a2d51b0-64bc-41ea-86b1-8f3bcbb65a89\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"robinson@crusoe.com\"} {\"correlation_id\":\"9a2d51b0-64bc-41ea-86b1-8f3bcbb65a89\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":612182,\"team_id\":2,\"email\":\"adelina.petrova@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"9a2d51b0-64bc-41ea-86b1-8f3bcbb65a89\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":612182,\"participants_processed\":2,\"exact_matches\":1,\"domain_matches\":0,\"best_match_found\":true} {\"correlation_id\":\"9a2d51b0-64bc-41ea-86b1-8f3bcbb65a89\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612182} {\"correlation_id\":\"9a2d51b0-64bc-41ea-86b1-8f3bcbb65a89\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612182,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"9a2d51b0-64bc-41ea-86b1-8f3bcbb65a89\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [EmailSchedule] STARTING batch process {\"host\":\"docker_lamp_1\"} {\"correlation_id\":\"dceccba8-9e9c-476e-993d-809067884ebe\",\"trace_id\":\"66f8bba8-1712-464b-b25a-418f16736be2\"}\n[2026-05-11 11:06:17] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":612182,\"remote_search\":true,\"lead_id\":null,\"contact_id\":4487,\"account_id\":244,\"opportunity_id\":299,\"stage_id\":36} {\"correlation_id\":\"9a2d51b0-64bc-41ea-86b1-8f3bcbb65a89\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [EmailSchedule] FINISHED batch process {\"host\":\"docker_lamp_1\",\"processed\":0} {\"correlation_id\":\"dceccba8-9e9c-476e-993d-809067884ebe\",\"trace_id\":\"66f8bba8-1712-464b-b25a-418f16736be2\"}\n[2026-05-11 11:06:17] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage for command {\"command\":\"mailbox:batch:process\",\"memoryBeforeCommandInMb\":60.0,\"memoryAfterCommandInMB\":60.0,\"memoryPeakBeforeCommandInMb\":99.727,\"memoryPeakAfterCommandInMB\":99.727} {\"correlation_id\":\"dceccba8-9e9c-476e-993d-809067884ebe\",\"trace_id\":\"66f8bba8-1712-464b-b25a-418f16736be2\"}\n[2026-05-11 11:06:17] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":612181,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":4487,\"account_id\":244,\"opportunity_id\":299,\"stage_id\":36}} {\"correlation_id\":\"8ff40f83-337f-432a-b950-e35779bb59bf\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612181} {\"correlation_id\":\"8ff40f83-337f-432a-b950-e35779bb59bf\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612181,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"8ff40f83-337f-432a-b950-e35779bb59bf\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":612181,\"participants\":[{\"id\":999223,\"user_id\":null,\"contact_id\":4487,\"lead_id\":null},{\"id\":999224,\"user_id\":261,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"8ff40f83-337f-432a-b950-e35779bb59bf\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"8ff40f83-337f-432a-b950-e35779bb59bf\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"8ff40f83-337f-432a-b950-e35779bb59bf\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"8ff40f83-337f-432a-b950-e35779bb59bf\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"8ff40f83-337f-432a-b950-e35779bb59bf\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"robinson@crusoe.com\"} {\"correlation_id\":\"8ff40f83-337f-432a-b950-e35779bb59bf\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":612181,\"team_id\":2,\"email\":\"adelina.petrova@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"8ff40f83-337f-432a-b950-e35779bb59bf\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":612181,\"participants_processed\":2,\"exact_matches\":1,\"domain_matches\":0,\"best_match_found\":true} {\"correlation_id\":\"8ff40f83-337f-432a-b950-e35779bb59bf\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612181} {\"correlation_id\":\"8ff40f83-337f-432a-b950-e35779bb59bf\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612181,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"8ff40f83-337f-432a-b950-e35779bb59bf\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":612181,\"remote_search\":true,\"lead_id\":null,\"contact_id\":4487,\"account_id\":244,\"opportunity_id\":299,\"stage_id\":36} {\"correlation_id\":\"8ff40f83-337f-432a-b950-e35779bb59bf\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":612180,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":4487,\"account_id\":244,\"opportunity_id\":299,\"stage_id\":36}} {\"correlation_id\":\"03f05b91-f803-45c9-b4bf-6a5f8ddf4a2c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612180} {\"correlation_id\":\"03f05b91-f803-45c9-b4bf-6a5f8ddf4a2c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612180,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"03f05b91-f803-45c9-b4bf-6a5f8ddf4a2c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":612180,\"participants\":[{\"id\":999221,\"user_id\":null,\"contact_id\":4487,\"lead_id\":null},{\"id\":999222,\"user_id\":261,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"03f05b91-f803-45c9-b4bf-6a5f8ddf4a2c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"03f05b91-f803-45c9-b4bf-6a5f8ddf4a2c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"03f05b91-f803-45c9-b4bf-6a5f8ddf4a2c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"03f05b91-f803-45c9-b4bf-6a5f8ddf4a2c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"03f05b91-f803-45c9-b4bf-6a5f8ddf4a2c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"robinson@crusoe.com\"} {\"correlation_id\":\"03f05b91-f803-45c9-b4bf-6a5f8ddf4a2c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":612180,\"team_id\":2,\"email\":\"adelina.petrova@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"03f05b91-f803-45c9-b4bf-6a5f8ddf4a2c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":612180,\"participants_processed\":2,\"exact_matches\":1,\"domain_matches\":0,\"best_match_found\":true} {\"correlation_id\":\"03f05b91-f803-45c9-b4bf-6a5f8ddf4a2c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612180} {\"correlation_id\":\"03f05b91-f803-45c9-b4bf-6a5f8ddf4a2c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612180,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"03f05b91-f803-45c9-b4bf-6a5f8ddf4a2c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":612180,\"remote_search\":true,\"lead_id\":null,\"contact_id\":4487,\"account_id\":244,\"opportunity_id\":299,\"stage_id\":36} {\"correlation_id\":\"03f05b91-f803-45c9-b4bf-6a5f8ddf4a2c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":611455,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89}} {\"correlation_id\":\"299c67e4-7026-46ee-b10d-d4e48fb55a7c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":611455} {\"correlation_id\":\"299c67e4-7026-46ee-b10d-d4e48fb55a7c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":611455,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"299c67e4-7026-46ee-b10d-d4e48fb55a7c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":611455,\"participants\":[{\"id\":997961,\"user_id\":null,\"contact_id\":null,\"lead_id\":null},{\"id\":997962,\"user_id\":1460,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"299c67e4-7026-46ee-b10d-d4e48fb55a7c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"299c67e4-7026-46ee-b10d-d4e48fb55a7c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"299c67e4-7026-46ee-b10d-d4e48fb55a7c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"299c67e4-7026-46ee-b10d-d4e48fb55a7c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"299c67e4-7026-46ee-b10d-d4e48fb55a7c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [Prospect match] Cache miss, calling the API {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"299c67e4-7026-46ee-b10d-d4e48fb55a7c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [Hubspot] Failed to fetch contact {\"email\":\"support@staging.jiminny.com\",\"reason\":\"[404] Client error: `GET https://api.hubapi.com/crm/v3/objects/contacts/support%40staging.jiminny.com?properties=email%2Cfirstname%2Clastname%2Ccountry%2Cphone%2Cmobilephone%2Cjobtitle%2Chubspot_owner_id%2Cassociatedcompanyid%2Cphoto&archived=0&idProperty=email` resulted in a `404 Not Found` response\"} {\"correlation_id\":\"299c67e4-7026-46ee-b10d-d4e48fb55a7c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [Prospect match] API returned empty result, caching the miss with empty prospect data {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"299c67e4-7026-46ee-b10d-d4e48fb55a7c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [Hubspot] Pagination completed {\"team_id\":2,\"endpoint\":\"https://api.hubapi.com/crm/v3/objects/contact/search\",\"total_requests\":1,\"total_records_fetched\":0,\"total_elapsed_seconds\":0.24,\"average_seconds_per_request\":0.24} {\"correlation_id\":\"299c67e4-7026-46ee-b10d-d4e48fb55a7c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [Prospect match] Resolved company domain from email {\"email\":\"support@staging.jiminny.com\",\"domain\":\"jiminny.com\"} {\"correlation_id\":\"299c67e4-7026-46ee-b10d-d4e48fb55a7c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [Prospect match] Cache miss {\"identifier_type\":\"domain\",\"identifier\":\"jiminny.com\",\"crm\":\"hubspot\"} {\"correlation_id\":\"299c67e4-7026-46ee-b10d-d4e48fb55a7c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [Prospect match] Cache miss, calling the API {\"identifier_type\":\"domain\",\"identifier\":\"jiminny.com\"} {\"correlation_id\":\"299c67e4-7026-46ee-b10d-d4e48fb55a7c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:19] local.INFO: [HubSpot] importAccount {\"crm_provider_id\":\"749766179\",\"config_id\":2} {\"correlation_id\":\"299c67e4-7026-46ee-b10d-d4e48fb55a7c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:19] local.INFO: [HubSpot] CRM Search requested {\"request\":{\"filterGroups\":[{\"filters\":[{\"propertyName\":\"associations.company\",\"operator\":\"EQ\",\"value\":\"749766179\"},{\"propertyName\":\"dealstage\",\"operator\":\"NOT_IN\",\"values\":[\"closedwon\",\"4040964\",\"59247967\"]},{\"propertyName\":\"dealstage\",\"operator\":\"NOT_IN\",\"values\":[\"closedlost\",\"4040965\",\"59247968\"]}]}],\"sorts\":[{\"propertyName\":\"modifieddate\",\"direction\":\"DESCENDING\"}],\"properties\":[\"dealname\",\"amount\",\"hubspot_owner_id\",\"pipeline\",\"dealstage\",\"closedate\",\"deal_currency_code\"],\"limit\":200}} {\"correlation_id\":\"299c67e4-7026-46ee-b10d-d4e48fb55a7c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:19] local.INFO: [Hubspot] Pagination completed {\"team_id\":2,\"endpoint\":\"https://api.hubapi.com/crm/v3/objects/deals/search\",\"total_requests\":1,\"total_records_fetched\":10,\"total_elapsed_seconds\":0.34,\"average_seconds_per_request\":0.34} {\"correlation_id\":\"299c67e4-7026-46ee-b10d-d4e48fb55a7c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:20] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":611455,\"team_id\":2,\"email\":\"aneliya.angelova@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"299c67e4-7026-46ee-b10d-d4e48fb55a7c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:21] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":611455,\"participants_processed\":2,\"exact_matches\":0,\"domain_matches\":1,\"best_match_found\":true} {\"correlation_id\":\"299c67e4-7026-46ee-b10d-d4e48fb55a7c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:21] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":611455} {\"correlation_id\":\"299c67e4-7026-46ee-b10d-d4e48fb55a7c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:21] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":611455,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"299c67e4-7026-46ee-b10d-d4e48fb55a7c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:21] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":611455,\"remote_search\":true,\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89} {\"correlation_id\":\"299c67e4-7026-46ee-b10d-d4e48fb55a7c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:21] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":611451,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89}} {\"correlation_id\":\"2385adb3-c893-49f8-9fbc-65b354009bea\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:21] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":611451} {\"correlation_id\":\"2385adb3-c893-49f8-9fbc-65b354009bea\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:21] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":611451,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"2385adb3-c893-49f8-9fbc-65b354009bea\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:21] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":611451,\"participants\":[{\"id\":997955,\"user_id\":null,\"contact_id\":null,\"lead_id\":null},{\"id\":997956,\"user_id\":18,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"2385adb3-c893-49f8-9fbc-65b354009bea\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:21] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"2385adb3-c893-49f8-9fbc-65b354009bea\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:21] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"2385adb3-c893-49f8-9fbc-65b354009bea\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:21] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"2385adb3-c893-49f8-9fbc-65b354009bea\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:21] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"2385adb3-c893-49f8-9fbc-65b354009bea\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:21] local.INFO: [Prospect match] Cache miss, calling the API {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"2385adb3-c893-49f8-9fbc-65b354009bea\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:23] local.INFO: [Hubspot] Failed to fetch contact {\"email\":\"support@staging.jiminny.com\",\"reason\":\"[404] Client error: `GET https://api.hubapi.com/crm/v3/objects/contacts/support%40staging.jiminny.com?properties=email%2Cfirstname%2Clastname%2Ccountry%2Cphone%2Cmobilephone%2Cjobtitle%2Chubspot_owner_id%2Cassociatedcompanyid%2Cphoto&archived=0&idProperty=email` resulted in a `404 Not Found` response\"} {\"correlation_id\":\"2385adb3-c893-49f8-9fbc-65b354009bea\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:23] local.INFO: [Prospect match] API returned empty result, caching the miss with empty prospect data {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"2385adb3-c893-49f8-9fbc-65b354009bea\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:23] local.INFO: [Hubspot] Pagination completed {\"team_id\":2,\"endpoint\":\"https://api.hubapi.com/crm/v3/objects/contact/search\",\"total_requests\":1,\"total_records_fetched\":0,\"total_elapsed_seconds\":0.49,\"average_seconds_per_request\":0.49} {\"correlation_id\":\"2385adb3-c893-49f8-9fbc-65b354009bea\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:24] local.INFO: [Prospect match] Resolved company domain from email {\"email\":\"support@staging.jiminny.com\",\"domain\":\"jiminny.com\"} {\"correlation_id\":\"2385adb3-c893-49f8-9fbc-65b354009bea\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:24] local.INFO: [Prospect match] Cache miss {\"identifier_type\":\"domain\",\"identifier\":\"jiminny.com\",\"crm\":\"hubspot\"} {\"correlation_id\":\"2385adb3-c893-49f8-9fbc-65b354009bea\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:24] local.INFO: [Prospect match] Cache miss, calling the API {\"identifier_type\":\"domain\",\"identifier\":\"jiminny.com\"} {\"correlation_id\":\"2385adb3-c893-49f8-9fbc-65b354009bea\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:24] local.INFO: [HubSpot] importAccount {\"crm_provider_id\":\"749766179\",\"config_id\":2} {\"correlation_id\":\"2385adb3-c893-49f8-9fbc-65b354009bea\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:24] local.INFO: [HubSpot] CRM Search requested {\"request\":{\"filterGroups\":[{\"filters\":[{\"propertyName\":\"associations.company\",\"operator\":\"EQ\",\"value\":\"749766179\"},{\"propertyName\":\"dealstage\",\"operator\":\"NOT_IN\",\"values\":[\"closedwon\",\"4040964\",\"59247967\"]},{\"propertyName\":\"dealstage\",\"operator\":\"NOT_IN\",\"values\":[\"closedlost\",\"4040965\",\"59247968\"]}]}],\"sorts\":[{\"propertyName\":\"modifieddate\",\"direction\":\"DESCENDING\"}],\"properties\":[\"dealname\",\"amount\",\"hubspot_owner_id\",\"pipeline\",\"dealstage\",\"closedate\",\"deal_currency_code\"],\"limit\":200}} {\"correlation_id\":\"2385adb3-c893-49f8-9fbc-65b354009bea\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:24] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"activity:purge-stale\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.727} {\"correlation_id\":\"1b5b216e-e0a9-4898-9f26-180cd26a0438\",\"trace_id\":\"f8d4ad10-fc79-469f-a5b8-e89d8fe8c392\"}\n[2026-05-11 11:06:24] local.INFO: [ EsUpdateProcessManager ] Finished updating entities in ES {\"worker\":\"\",\"peak_memory\":\"99.73 MB\",\"elapsed_seconds\":3.18,\"update_target\":\"activities\",\"should_iterate_again\":false} {\"correlation_id\":\"38935ae1-fcb3-437e-9c9e-1b7415a0b7e1\",\"trace_id\":\"325a2f9b-9c66-4223-ac02-1a43723f4315\"}\n[2026-05-11 11:06:24] local.INFO: [Hubspot] Pagination completed {\"team_id\":2,\"endpoint\":\"https://api.hubapi.com/crm/v3/objects/deals/search\",\"total_requests\":1,\"total_records_fetched\":10,\"total_elapsed_seconds\":0.26,\"average_seconds_per_request\":0.26} {\"correlation_id\":\"2385adb3-c893-49f8-9fbc-65b354009bea\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:25] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage for command {\"command\":\"activity:purge-stale\",\"memoryBeforeCommandInMb\":60.0,\"memoryAfterCommandInMB\":60.0,\"memoryPeakBeforeCommandInMb\":99.727,\"memoryPeakAfterCommandInMB\":99.727} {\"correlation_id\":\"1b5b216e-e0a9-4898-9f26-180cd26a0438\",\"trace_id\":\"f8d4ad10-fc79-469f-a5b8-e89d8fe8c392\"}\n[2026-05-11 11:06:25] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":611451,\"team_id\":2,\"email\":\"veselin.kulov@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"2385adb3-c893-49f8-9fbc-65b354009bea\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:25] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":611451,\"participants_processed\":2,\"exact_matches\":0,\"domain_matches\":1,\"best_match_found\":true} {\"correlation_id\":\"2385adb3-c893-49f8-9fbc-65b354009bea\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:25] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":611451} {\"correlation_id\":\"2385adb3-c893-49f8-9fbc-65b354009bea\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:25] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":611451,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"2385adb3-c893-49f8-9fbc-65b354009bea\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:25] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":611451,\"remote_search\":true,\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89} {\"correlation_id\":\"2385adb3-c893-49f8-9fbc-65b354009bea\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":611087,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89}} {\"correlation_id\":\"a6d1c39e-8bb7-43da-81ba-7a22dc8de463\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":611087} {\"correlation_id\":\"a6d1c39e-8bb7-43da-81ba-7a22dc8de463\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":611087,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"a6d1c39e-8bb7-43da-81ba-7a22dc8de463\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":611087,\"participants\":[{\"id\":997368,\"user_id\":null,\"contact_id\":null,\"lead_id\":null},{\"id\":997369,\"user_id\":18,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"a6d1c39e-8bb7-43da-81ba-7a22dc8de463\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"a6d1c39e-8bb7-43da-81ba-7a22dc8de463\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"a6d1c39e-8bb7-43da-81ba-7a22dc8de463\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"a6d1c39e-8bb7-43da-81ba-7a22dc8de463\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"a6d1c39e-8bb7-43da-81ba-7a22dc8de463\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"a6d1c39e-8bb7-43da-81ba-7a22dc8de463\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [Prospect match] cached empty result - no API calls, try next matching method {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"a6d1c39e-8bb7-43da-81ba-7a22dc8de463\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [Prospect match] Resolved company domain from email {\"email\":\"support@staging.jiminny.com\",\"domain\":\"jiminny.com\"} {\"correlation_id\":\"a6d1c39e-8bb7-43da-81ba-7a22dc8de463\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":611087,\"team_id\":2,\"email\":\"veselin.kulov@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"a6d1c39e-8bb7-43da-81ba-7a22dc8de463\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":611087,\"participants_processed\":2,\"exact_matches\":0,\"domain_matches\":1,\"best_match_found\":true} {\"correlation_id\":\"a6d1c39e-8bb7-43da-81ba-7a22dc8de463\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":611087} {\"correlation_id\":\"a6d1c39e-8bb7-43da-81ba-7a22dc8de463\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":611087,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"a6d1c39e-8bb7-43da-81ba-7a22dc8de463\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":611087,\"remote_search\":true,\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89} {\"correlation_id\":\"a6d1c39e-8bb7-43da-81ba-7a22dc8de463\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":611076,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89}} {\"correlation_id\":\"8905b485-a3d2-474a-a2dd-dd5375deb178\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":611076} {\"correlation_id\":\"8905b485-a3d2-474a-a2dd-dd5375deb178\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":611076,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"8905b485-a3d2-474a-a2dd-dd5375deb178\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":611076,\"participants\":[{\"id\":997346,\"user_id\":null,\"contact_id\":null,\"lead_id\":null},{\"id\":997347,\"user_id\":18,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"8905b485-a3d2-474a-a2dd-dd5375deb178\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"8905b485-a3d2-474a-a2dd-dd5375deb178\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"8905b485-a3d2-474a-a2dd-dd5375deb178\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"8905b485-a3d2-474a-a2dd-dd5375deb178\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"8905b485-a3d2-474a-a2dd-dd5375deb178\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"8905b485-a3d2-474a-a2dd-dd5375deb178\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [Prospect match] cached empty result - no API calls, try next matching method {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"8905b485-a3d2-474a-a2dd-dd5375deb178\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [Prospect match] Resolved company domain from email {\"email\":\"support@staging.jiminny.com\",\"domain\":\"jiminny.com\"} {\"correlation_id\":\"8905b485-a3d2-474a-a2dd-dd5375deb178\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":611076,\"team_id\":2,\"email\":\"veselin.kulov@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"8905b485-a3d2-474a-a2dd-dd5375deb178\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":611076,\"participants_processed\":2,\"exact_matches\":0,\"domain_matches\":1,\"best_match_found\":true} {\"correlation_id\":\"8905b485-a3d2-474a-a2dd-dd5375deb178\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":611076} {\"correlation_id\":\"8905b485-a3d2-474a-a2dd-dd5375deb178\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":611076,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"8905b485-a3d2-474a-a2dd-dd5375deb178\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":611076,\"remote_search\":true,\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89} {\"correlation_id\":\"8905b485-a3d2-474a-a2dd-dd5375deb178\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":610935,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89}} {\"correlation_id\":\"77fcbcd0-06e6-4680-85f5-cdefdd76007c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610935} {\"correlation_id\":\"77fcbcd0-06e6-4680-85f5-cdefdd76007c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610935,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"77fcbcd0-06e6-4680-85f5-cdefdd76007c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":610935,\"participants\":[{\"id\":997141,\"user_id\":null,\"contact_id\":null,\"lead_id\":null},{\"id\":997142,\"user_id\":18,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"77fcbcd0-06e6-4680-85f5-cdefdd76007c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"77fcbcd0-06e6-4680-85f5-cdefdd76007c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"77fcbcd0-06e6-4680-85f5-cdefdd76007c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"77fcbcd0-06e6-4680-85f5-cdefdd76007c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"77fcbcd0-06e6-4680-85f5-cdefdd76007c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"77fcbcd0-06e6-4680-85f5-cdefdd76007c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [Prospect match] cached empty result - no API calls, try next matching method {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"77fcbcd0-06e6-4680-85f5-cdefdd76007c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [Prospect match] Resolved company domain from email {\"email\":\"support@staging.jiminny.com\",\"domain\":\"jiminny.com\"} {\"correlation_id\":\"77fcbcd0-06e6-4680-85f5-cdefdd76007c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":610935,\"team_id\":2,\"email\":\"veselin.kulov@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"77fcbcd0-06e6-4680-85f5-cdefdd76007c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":610935,\"participants_processed\":2,\"exact_matches\":0,\"domain_matches\":1,\"best_match_found\":true} {\"correlation_id\":\"77fcbcd0-06e6-4680-85f5-cdefdd76007c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610935} {\"correlation_id\":\"77fcbcd0-06e6-4680-85f5-cdefdd76007c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610935,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"77fcbcd0-06e6-4680-85f5-cdefdd76007c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":610935,\"remote_search\":true,\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89} {\"correlation_id\":\"77fcbcd0-06e6-4680-85f5-cdefdd76007c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":610915,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89}} {\"correlation_id\":\"79639904-266b-4873-8940-014db94b5d8f\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610915} {\"correlation_id\":\"79639904-266b-4873-8940-014db94b5d8f\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610915,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"79639904-266b-4873-8940-014db94b5d8f\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":610915,\"participants\":[{\"id\":997104,\"user_id\":null,\"contact_id\":null,\"lead_id\":null},{\"id\":997105,\"user_id\":18,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"79639904-266b-4873-8940-014db94b5d8f\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"79639904-266b-4873-8940-014db94b5d8f\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"79639904-266b-4873-8940-014db94b5d8f\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"79639904-266b-4873-8940-014db94b5d8f\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"79639904-266b-4873-8940-014db94b5d8f\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"79639904-266b-4873-8940-014db94b5d8f\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [Prospect match] cached empty result - no API calls, try next matching method {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"79639904-266b-4873-8940-014db94b5d8f\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [Prospect match] Resolved company domain from email {\"email\":\"support@staging.jiminny.com\",\"domain\":\"jiminny.com\"} {\"correlation_id\":\"79639904-266b-4873-8940-014db94b5d8f\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":610915,\"team_id\":2,\"email\":\"veselin.kulov@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"79639904-266b-4873-8940-014db94b5d8f\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":610915,\"participants_processed\":2,\"exact_matches\":0,\"domain_matches\":1,\"best_match_found\":true} {\"correlation_id\":\"79639904-266b-4873-8940-014db94b5d8f\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610915} {\"correlation_id\":\"79639904-266b-4873-8940-014db94b5d8f\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610915,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"79639904-266b-4873-8940-014db94b5d8f\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":610915,\"remote_search\":true,\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89} {\"correlation_id\":\"79639904-266b-4873-8940-014db94b5d8f\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":610900,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89}} {\"correlation_id\":\"068ab494-a35a-4562-b447-44bf2ddb775d\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610900} {\"correlation_id\":\"068ab494-a35a-4562-b447-44bf2ddb775d\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610900,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"068ab494-a35a-4562-b447-44bf2ddb775d\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":610900,\"participants\":[{\"id\":997081,\"user_id\":null,\"contact_id\":null,\"lead_id\":null},{\"id\":997082,\"user_id\":18,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"068ab494-a35a-4562-b447-44bf2ddb775d\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"068ab494-a35a-4562-b447-44bf2ddb775d\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"068ab494-a35a-4562-b447-44bf2ddb775d\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"068ab494-a35a-4562-b447-44bf2ddb775d\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"068ab494-a35a-4562-b447-44bf2ddb775d\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"068ab494-a35a-4562-b447-44bf2ddb775d\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [Prospect match] cached empty result - no API calls, try next matching method {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"068ab494-a35a-4562-b447-44bf2ddb775d\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [Prospect match] Resolved company domain from email {\"email\":\"support@staging.jiminny.com\",\"domain\":\"jiminny.com\"} {\"correlation_id\":\"068ab494-a35a-4562-b447-44bf2ddb775d\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":610900,\"team_id\":2,\"email\":\"veselin.kulov@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"068ab494-a35a-4562-b447-44bf2ddb775d\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":610900,\"participants_processed\":2,\"exact_matches\":0,\"domain_matches\":1,\"best_match_found\":true} {\"correlation_id\":\"068ab494-a35a-4562-b447-44bf2ddb775d\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610900} {\"correlation_id\":\"068ab494-a35a-4562-b447-44bf2ddb775d\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610900,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"068ab494-a35a-4562-b447-44bf2ddb775d\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":610900,\"remote_search\":true,\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89} {\"correlation_id\":\"068ab494-a35a-4562-b447-44bf2ddb775d\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":610885,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89}} {\"correlation_id\":\"a556c8c6-d98c-4c17-b45c-e5ba57a68062\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610885} {\"correlation_id\":\"a556c8c6-d98c-4c17-b45c-e5ba57a68062\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610885,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"a556c8c6-d98c-4c17-b45c-e5ba57a68062\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":610885,\"participants\":[{\"id\":997051,\"user_id\":null,\"contact_id\":null,\"lead_id\":null},{\"id\":997052,\"user_id\":18,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"a556c8c6-d98c-4c17-b45c-e5ba57a68062\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"a556c8c6-d98c-4c17-b45c-e5ba57a68062\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"a556c8c6-d98c-4c17-b45c-e5ba57a68062\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"a556c8c6-d98c-4c17-b45c-e5ba57a68062\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"a556c8c6-d98c-4c17-b45c-e5ba57a68062\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"a556c8c6-d98c-4c17-b45c-e5ba57a68062\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [Prospect match] cached empty result - no API calls, try next matching method {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"a556c8c6-d98c-4c17-b45c-e5ba57a68062\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [Prospect match] Resolved company domain from email {\"email\":\"support@staging.jiminny.com\",\"domain\":\"jiminny.com\"} {\"correlation_id\":\"a556c8c6-d98c-4c17-b45c-e5ba57a68062\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":610885,\"team_id\":2,\"email\":\"veselin.kulov@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"a556c8c6-d98c-4c17-b45c-e5ba57a68062\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":610885,\"participants_processed\":2,\"exact_matches\":0,\"domain_matches\":1,\"best_match_found\":true} {\"correlation_id\":\"a556c8c6-d98c-4c17-b45c-e5ba57a68062\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610885} {\"correlation_id\":\"a556c8c6-d98c-4c17-b45c-e5ba57a68062\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610885,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"a556c8c6-d98c-4c17-b45c-e5ba57a68062\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":610885,\"remote_search\":true,\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89} {\"correlation_id\":\"a556c8c6-d98c-4c17-b45c-e5ba57a68062\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":610878,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89}} {\"correlation_id\":\"915d4deb-56d3-4de7-bc74-2274b327505c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610878} {\"correlation_id\":\"915d4deb-56d3-4de7-bc74-2274b327505c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610878,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"915d4deb-56d3-4de7-bc74-2274b327505c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":610878,\"participants\":[{\"id\":997035,\"user_id\":null,\"contact_id\":null,\"lead_id\":null},{\"id\":997036,\"user_id\":18,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"915d4deb-56d3-4de7-bc74-2274b327505c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"915d4deb-56d3-4de7-bc74-2274b327505c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"915d4deb-56d3-4de7-bc74-2274b327505c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"915d4deb-56d3-4de7-bc74-2274b327505c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"915d4deb-56d3-4de7-bc74-2274b327505c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"915d4deb-56d3-4de7-bc74-2274b327505c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [Prospect match] cached empty result - no API calls, try next matching method {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"915d4deb-56d3-4de7-bc74-2274b327505c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [Prospect match] Resolved company domain from email {\"email\":\"support@staging.jiminny.com\",\"domain\":\"jiminny.com\"} {\"correlation_id\":\"915d4deb-56d3-4de7-bc74-2274b327505c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":610878,\"team_id\":2,\"email\":\"veselin.kulov@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"915d4deb-56d3-4de7-bc74-2274b327505c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":610878,\"participants_processed\":2,\"exact_matches\":0,\"domain_matches\":1,\"best_match_found\":true} {\"correlation_id\":\"915d4deb-56d3-4de7-bc74-2274b327505c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610878} {\"correlation_id\":\"915d4deb-56d3-4de7-bc74-2274b327505c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610878,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"915d4deb-56d3-4de7-bc74-2274b327505c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":610878,\"remote_search\":true,\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89} {\"correlation_id\":\"915d4deb-56d3-4de7-bc74-2274b327505c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":610874,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89}} {\"correlation_id\":\"1b89c98c-65d3-4286-b140-c9453bca9a74\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610874} {\"correlation_id\":\"1b89c98c-65d3-4286-b140-c9453bca9a74\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610874,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"1b89c98c-65d3-4286-b140-c9453bca9a74\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":610874,\"participants\":[{\"id\":997025,\"user_id\":null,\"contact_id\":null,\"lead_id\":null},{\"id\":997026,\"user_id\":18,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"1b89c98c-65d3-4286-b140-c9453bca9a74\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"1b89c98c-65d3-4286-b140-c9453bca9a74\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"1b89c98c-65d3-4286-b140-c9453bca9a74\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"1b89c98c-65d3-4286-b140-c9453bca9a74\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"1b89c98c-65d3-4286-b140-c9453bca9a74\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"1b89c98c-65d3-4286-b140-c9453bca9a74\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [Prospect match] cached empty result - no API calls, try next matching method {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"1b89c98c-65d3-4286-b140-c9453bca9a74\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [Prospect match] Resolved company domain from email {\"email\":\"support@staging.jiminny.com\",\"domain\":\"jiminny.com\"} {\"correlation_id\":\"1b89c98c-65d3-4286-b140-c9453bca9a74\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":610874,\"team_id\":2,\"email\":\"veselin.kulov@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"1b89c98c-65d3-4286-b140-c9453bca9a74\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":610874,\"participants_processed\":2,\"exact_matches\":0,\"domain_matches\":1,\"best_match_found\":true} {\"correlation_id\":\"1b89c98c-65d3-4286-b140-c9453bca9a74\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610874} {\"correlation_id\":\"1b89c98c-65d3-4286-b140-c9453bca9a74\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610874,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"1b89c98c-65d3-4286-b140-c9453bca9a74\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":610874,\"remote_search\":true,\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89} {\"correlation_id\":\"1b89c98c-65d3-4286-b140-c9453bca9a74\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":610867,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89}} {\"correlation_id\":\"8a33657f-373a-4b5d-b1f9-71bbe85bd3ba\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610867} {\"correlation_id\":\"8a33657f-373a-4b5d-b1f9-71bbe85bd3ba\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610867,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"8a33657f-373a-4b5d-b1f9-71bbe85bd3ba\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":610867,\"participants\":[{\"id\":997011,\"user_id\":null,\"contact_id\":null,\"lead_id\":null},{\"id\":997012,\"user_id\":18,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"8a33657f-373a-4b5d-b1f9-71bbe85bd3ba\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"8a33657f-373a-4b5d-b1f9-71bbe85bd3ba\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"8a33657f-373a-4b5d-b1f9-71bbe85bd3ba\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"8a33657f-373a-4b5d-b1f9-71bbe85bd3ba\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"8a33657f-373a-4b5d-b1f9-71bbe85bd3ba\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"8a33657f-373a-4b5d-b1f9-71bbe85bd3ba\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [Prospect match] cached empty result - no API calls, try next matching method {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"8a33657f-373a-4b5d-b1f9-71bbe85bd3ba\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [Prospect match] Resolved company domain from email {\"email\":\"support@staging.jiminny.com\",\"domain\":\"jiminny.com\"} {\"correlation_id\":\"8a33657f-373a-4b5d-b1f9-71bbe85bd3ba\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":610867,\"team_id\":2,\"email\":\"veselin.kulov@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"8a33657f-373a-4b5d-b1f9-71bbe85bd3ba\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":610867,\"participants_processed\":2,\"exact_matches\":0,\"domain_matches\":1,\"best_match_found\":true} {\"correlation_id\":\"8a33657f-373a-4b5d-b1f9-71bbe85bd3ba\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610867} {\"correlation_id\":\"8a33657f-373a-4b5d-b1f9-71bbe85bd3ba\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610867,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"8a33657f-373a-4b5d-b1f9-71bbe85bd3ba\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":610867,\"remote_search\":true,\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89} {\"correlation_id\":\"8a33657f-373a-4b5d-b1f9-71bbe85bd3ba\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":610764,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89}} {\"correlation_id\":\"440ce0fb-99c1-458c-9db4-c11e504dd620\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610764} {\"correlation_id\":\"440ce0fb-99c1-458c-9db4-c11e504dd620\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610764,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"440ce0fb-99c1-458c-9db4-c11e504dd620\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":610764,\"participants\":[{\"id\":996951,\"user_id\":null,\"contact_id\":null,\"lead_id\":null},{\"id\":996952,\"user_id\":18,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"440ce0fb-99c1-458c-9db4-c11e504dd620\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"440ce0fb-99c1-458c-9db4-c11e504dd620\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"440ce0fb-99c1-458c-9db4-c11e504dd620\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"440ce0fb-99c1-458c-9db4-c11e504dd620\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"440ce0fb-99c1-458c-9db4-c11e504dd620\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"440ce0fb-99c1-458c-9db4-c11e504dd620\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [Prospect match] cached empty result - no API calls, try next matching method {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"440ce0fb-99c1-458c-9db4-c11e504dd620\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [Prospect match] Resolved company domain from email {\"email\":\"support@staging.jiminny.com\",\"domain\":\"jiminny.com\"} {\"correlation_id\":\"440ce0fb-99c1-458c-9db4-c11e504dd620\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":610764,\"team_id\":2,\"email\":\"veselin.kulov@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"440ce0fb-99c1-458c-9db4-c11e504dd620\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":610764,\"participants_processed\":2,\"exact_matches\":0,\"domain_matches\":1,\"best_match_found\":true} {\"correlation_id\":\"440ce0fb-99c1-458c-9db4-c11e504dd620\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610764} {\"correlation_id\":\"440ce0fb-99c1-458c-9db4-c11e504dd620\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610764,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"440ce0fb-99c1-458c-9db4-c11e504dd620\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":610764,\"remote_search\":true,\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89} {\"correlation_id\":\"440ce0fb-99c1-458c-9db4-c11e504dd620\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":610617,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89}} {\"correlation_id\":\"d8853964-8fe9-4553-96da-d088b6dfc843\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610617} {\"correlation_id\":\"d8853964-8fe9-4553-96da-d088b6dfc843\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610617,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"d8853964-8fe9-4553-96da-d088b6dfc843\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":610617,\"participants\":[{\"id\":996641,\"user_id\":null,\"contact_id\":null,\"lead_id\":null},{\"id\":996642,\"user_id\":18,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"d8853964-8fe9-4553-96da-d088b6dfc843\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"d8853964-8fe9-4553-96da-d088b6dfc843\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"d8853964-8fe9-4553-96da-d088b6dfc843\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"d8853964-8fe9-4553-96da-d088b6dfc843\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"d8853964-8fe9-4553-96da-d088b6dfc843\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"d8853964-8fe9-4553-96da-d088b6dfc843\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [Prospect match] cached empty result - no API calls, try next matching method {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"d8853964-8fe9-4553-96da-d088b6dfc843\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [Prospect match] Resolved company domain from email {\"email\":\"support@staging.jiminny.com\",\"domain\":\"jiminny.com\"} {\"correlation_id\":\"d8853964-8fe9-4553-96da-d088b6dfc843\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":610617,\"team_id\":2,\"email\":\"veselin.kulov@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"d8853964-8fe9-4553-96da-d088b6dfc843\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":610617,\"participants_processed\":2,\"exact_matches\":0,\"domain_matches\":1,\"best_match_found\":true} {\"correlation_id\":\"d8853964-8fe9-4553-96da-d088b6dfc843\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610617} {\"correlation_id\":\"d8853964-8fe9-4553-96da-d088b6dfc843\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610617,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"d8853964-8fe9-4553-96da-d088b6dfc843\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":610617,\"remote_search\":true,\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89} {\"correlation_id\":\"d8853964-8fe9-4553-96da-d088b6dfc843\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":610539,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89}} {\"correlation_id\":\"d021767c-298a-4ad0-9f72-dca085cc6528\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610539} {\"correlation_id\":\"d021767c-298a-4ad0-9f72-dca085cc6528\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610539,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"d021767c-298a-4ad0-9f72-dca085cc6528\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":610539,\"participants\":[{\"id\":996485,\"user_id\":null,\"contact_id\":null,\"lead_id\":null},{\"id\":996486,\"user_id\":18,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"d021767c-298a-4ad0-9f72-dca085cc6528\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"d021767c-298a-4ad0-9f72-dca085cc6528\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"d021767c-298a-4ad0-9f72-dca085cc6528\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"d021767c-298a-4ad0-9f72-dca085cc6528\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"d021767c-298a-4ad0-9f72-dca085cc6528\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"d021767c-298a-4ad0-9f72-dca085cc6528\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [Prospect match] cached empty result - no API calls, try next matching method {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"d021767c-298a-4ad0-9f72-dca085cc6528\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [Prospect match] Resolved company domain from email {\"email\":\"support@staging.jiminny.com\",\"domain\":\"jiminny.com\"} {\"correlation_id\":\"d021767c-298a-4ad0-9f72-dca085cc6528\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":610539,\"team_id\":2,\"email\":\"veselin.kulov@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"d021767c-298a-4ad0-9f72-dca085cc6528\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":610539,\"participants_processed\":2,\"exact_matches\":0,\"domain_matches\":1,\"best_match_found\":true} {\"correlation_id\":\"d021767c-298a-4ad0-9f72-dca085cc6528\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610539} {\"correlation_id\":\"d021767c-298a-4ad0-9f72-dca085cc6528\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610539,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"d021767c-298a-4ad0-9f72-dca085cc6528\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"mailbox:text-relay:sync\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.727} {\"correlation_id\":\"7c0c4db8-2e4e-4aa1-aa71-a3d63cdb81d7\",\"trace_id\":\"b3e9e2f5-3256-429a-ad76-e46e3c2e626d\"}\n[2026-05-11 11:06:28] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":610539,\"remote_search\":true,\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89} {\"correlation_id\":\"d021767c-298a-4ad0-9f72-dca085cc6528\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":610528,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89}} {\"correlation_id\":\"2410a4b8-a997-4cf1-99f0-c23ba54805c7\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610528} {\"correlation_id\":\"2410a4b8-a997-4cf1-99f0-c23ba54805c7\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610528,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"2410a4b8-a997-4cf1-99f0-c23ba54805c7\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":610528,\"participants\":[{\"id\":996463,\"user_id\":null,\"contact_id\":null,\"lead_id\":null},{\"id\":996464,\"user_id\":18,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"2410a4b8-a997-4cf1-99f0-c23ba54805c7\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"2410a4b8-a997-4cf1-99f0-c23ba54805c7\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"2410a4b8-a997-4cf1-99f0-c23ba54805c7\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"2410a4b8-a997-4cf1-99f0-c23ba54805c7\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"2410a4b8-a997-4cf1-99f0-c23ba54805c7\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"2410a4b8-a997-4cf1-99f0-c23ba54805c7\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [Prospect match] cached empty result - no API calls, try next matching method {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"2410a4b8-a997-4cf1-99f0-c23ba54805c7\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [Prospect match] Resolved company domain from email {\"email\":\"support@staging.jiminny.com\",\"domain\":\"jiminny.com\"} {\"correlation_id\":\"2410a4b8-a997-4cf1-99f0-c23ba54805c7\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":610528,\"team_id\":2,\"email\":\"veselin.kulov@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"2410a4b8-a997-4cf1-99f0-c23ba54805c7\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":610528,\"participants_processed\":2,\"exact_matches\":0,\"domain_matches\":1,\"best_match_found\":true} {\"correlation_id\":\"2410a4b8-a997-4cf1-99f0-c23ba54805c7\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610528} {\"correlation_id\":\"2410a4b8-a997-4cf1-99f0-c23ba54805c7\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610528,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"2410a4b8-a997-4cf1-99f0-c23ba54805c7\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":610528,\"remote_search\":true,\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89} {\"correlation_id\":\"2410a4b8-a997-4cf1-99f0-c23ba54805c7\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":610506,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89}} {\"correlation_id\":\"8b5bce8c-7e89-46ba-b04b-9b9ab4e620b6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610506} {\"correlation_id\":\"8b5bce8c-7e89-46ba-b04b-9b9ab4e620b6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610506,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"8b5bce8c-7e89-46ba-b04b-9b9ab4e620b6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":610506,\"participants\":[{\"id\":996419,\"user_id\":null,\"contact_id\":null,\"lead_id\":null},{\"id\":996420,\"user_id\":18,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"8b5bce8c-7e89-46ba-b04b-9b9ab4e620b6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"8b5bce8c-7e89-46ba-b04b-9b9ab4e620b6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"8b5bce8c-7e89-46ba-b04b-9b9ab4e620b6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"8b5bce8c-7e89-46ba-b04b-9b9ab4e620b6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"8b5bce8c-7e89-46ba-b04b-9b9ab4e620b6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"8b5bce8c-7e89-46ba-b04b-9b9ab4e620b6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [Prospect match] cached empty result - no API calls, try next matching method {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"8b5bce8c-7e89-46ba-b04b-9b9ab4e620b6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [Prospect match] Resolved company domain from email {\"email\":\"support@staging.jiminny.com\",\"domain\":\"jiminny.com\"} {\"correlation_id\":\"8b5bce8c-7e89-46ba-b04b-9b9ab4e620b6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":610506,\"team_id\":2,\"email\":\"veselin.kulov@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"8b5bce8c-7e89-46ba-b04b-9b9ab4e620b6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":610506,\"participants_processed\":2,\"exact_matches\":0,\"domain_matches\":1,\"best_match_found\":true} {\"correlation_id\":\"8b5bce8c-7e89-46ba-b04b-9b9ab4e620b6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610506} {\"correlation_id\":\"8b5bce8c-7e89-46ba-b04b-9b9ab4e620b6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610506,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"8b5bce8c-7e89-46ba-b04b-9b9ab4e620b6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":610506,\"remote_search\":true,\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89} {\"correlation_id\":\"8b5bce8c-7e89-46ba-b04b-9b9ab4e620b6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":610497,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89}} {\"correlation_id\":\"a9e55e87-8951-406b-84a8-6fecf2841584\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610497} {\"correlation_id\":\"a9e55e87-8951-406b-84a8-6fecf2841584\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610497,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"a9e55e87-8951-406b-84a8-6fecf2841584\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":610497,\"participants\":[{\"id\":996401,\"user_id\":null,\"contact_id\":null,\"lead_id\":null},{\"id\":996402,\"user_id\":18,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"a9e55e87-8951-406b-84a8-6fecf2841584\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"a9e55e87-8951-406b-84a8-6fecf2841584\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"a9e55e87-8951-406b-84a8-6fecf2841584\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"a9e55e87-8951-406b-84a8-6fecf2841584\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"a9e55e87-8951-406b-84a8-6fecf2841584\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"a9e55e87-8951-406b-84a8-6fecf2841584\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [Prospect match] cached empty result - no API calls, try next matching method {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"a9e55e87-8951-406b-84a8-6fecf2841584\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [Prospect match] Resolved company domain from email {\"email\":\"support@staging.jiminny.com\",\"domain\":\"jiminny.com\"} {\"correlation_id\":\"a9e55e87-8951-406b-84a8-6fecf2841584\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":610497,\"team_id\":2,\"email\":\"veselin.kulov@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"a9e55e87-8951-406b-84a8-6fecf2841584\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":610497,\"participants_processed\":2,\"exact_matches\":0,\"domain_matches\":1,\"best_match_found\":true} {\"correlation_id\":\"a9e55e87-8951-406b-84a8-6fecf2841584\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610497} {\"correlation_id\":\"a9e55e87-8951-406b-84a8-6fecf2841584\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610497,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"a9e55e87-8951-406b-84a8-6fecf2841584\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":610497,\"remote_search\":true,\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89} {\"correlation_id\":\"a9e55e87-8951-406b-84a8-6fecf2841584\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":610490,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89}} {\"correlation_id\":\"209ccee3-3f37-4fb8-8648-ff2f554f6cf3\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610490} {\"correlation_id\":\"209ccee3-3f37-4fb8-8648-ff2f554f6cf3\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610490,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"209ccee3-3f37-4fb8-8648-ff2f554f6cf3\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":610490,\"participants\":[{\"id\":996385,\"user_id\":null,\"contact_id\":null,\"lead_id\":null},{\"id\":996386,\"user_id\":18,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"209ccee3-3f37-4fb8-8648-ff2f554f6cf3\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"209ccee3-3f37-4fb8-8648-ff2f554f6cf3\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"209ccee3-3f37-4fb8-8648-ff2f554f6cf3\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"209ccee3-3f37-4fb8-8648-ff2f554f6cf3\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"209ccee3-3f37-4fb8-8648-ff2f554f6cf3\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"209ccee3-3f37-4fb8-8648-ff2f554f6cf3\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [Prospect match] cached empty result - no API calls, try next matching method {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"209ccee3-3f37-4fb8-8648-ff2f554f6cf3\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [Prospect match] Resolved company domain from email {\"email\":\"support@staging.jiminny.com\",\"domain\":\"jiminny.com\"} {\"correlation_id\":\"209ccee3-3f37-4fb8-8648-ff2f554f6cf3\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":610490,\"team_id\":2,\"email\":\"veselin.kulov@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"209ccee3-3f37-4fb8-8648-ff2f554f6cf3\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":610490,\"participants_processed\":2,\"exact_matches\":0,\"domain_matches\":1,\"best_match_found\":true} {\"correlation_id\":\"209ccee3-3f37-4fb8-8648-ff2f554f6cf3\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610490} {\"correlation_id\":\"209ccee3-3f37-4fb8-8648-ff2f554f6cf3\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610490,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"209ccee3-3f37-4fb8-8648-ff2f554f6cf3\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":610490,\"remote_search\":true,\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89} {\"correlation_id\":\"209ccee3-3f37-4fb8-8648-ff2f554f6cf3\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":610470,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89}} {\"correlation_id\":\"0b6c4d04-c34f-4cfd-b391-aa0128a17792\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610470} {\"correlation_id\":\"0b6c4d04-c34f-4cfd-b391-aa0128a17792\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610470,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"0b6c4d04-c34f-4cfd-b391-aa0128a17792\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":610470,\"participants\":[{\"id\":996369,\"user_id\":null,\"contact_id\":null,\"lead_id\":null},{\"id\":996370,\"user_id\":18,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"0b6c4d04-c34f-4cfd-b391-aa0128a17792\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage for command {\"command\":\"mailbox:text-relay:sync\",\"memoryBeforeCommandInMb\":60.0,\"memoryAfterCommandInMB\":60.0,\"memoryPeakBeforeCommandInMb\":99.727,\"memoryPeakAfterCommandInMB\":99.727} {\"correlation_id\":\"7c0c4db8-2e4e-4aa1-aa71-a3d63cdb81d7\",\"trace_id\":\"b3e9e2f5-3256-429a-ad76-e46e3c2e626d\"}\n[2026-05-11 11:06:28] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"0b6c4d04-c34f-4cfd-b391-aa0128a17792\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"0b6c4d04-c34f-4cfd-b391-aa0128a17792\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"0b6c4d04-c34f-4cfd-b391-aa0128a17792\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"0b6c4d04-c34f-4cfd-b391-aa0128a17792\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"0b6c4d04-c34f-4cfd-b391-aa0128a17792\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [Prospect match] cached empty result - no API calls, try next matching method {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"0b6c4d04-c34f-4cfd-b391-aa0128a17792\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [Prospect match] Resolved company domain from email {\"email\":\"support@staging.jiminny.com\",\"domain\":\"jiminny.com\"} {\"correlation_id\":\"0b6c4d04-c34f-4cfd-b391-aa0128a17792\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":610470,\"team_id\":2,\"email\":\"veselin.kulov@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"0b6c4d04-c34f-4cfd-b391-aa0128a17792\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":610470,\"participants_processed\":2,\"exact_matches\":0,\"domain_matches\":1,\"best_match_found\":true} {\"correlation_id\":\"0b6c4d04-c34f-4cfd-b391-aa0128a17792\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610470} {\"correlation_id\":\"0b6c4d04-c34f-4cfd-b391-aa0128a17792\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610470,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"0b6c4d04-c34f-4cfd-b391-aa0128a17792\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":610470,\"remote_search\":true,\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89} {\"correlation_id\":\"0b6c4d04-c34f-4cfd-b391-aa0128a17792\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":610462,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89}} {\"correlation_id\":\"0584d557-927b-47a5-b193-04786c5c20bc\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610462} {\"correlation_id\":\"0584d557-927b-47a5-b193-04786c5c20bc\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610462,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"0584d557-927b-47a5-b193-04786c5c20bc\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":610462,\"participants\":[{\"id\":996353,\"user_id\":null,\"contact_id\":null,\"lead_id\":null},{\"id\":996354,\"user_id\":18,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"0584d557-927b-47a5-b193-04786c5c20bc\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"0584d557-927b-47a5-b193-04786c5c20bc\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"0584d557-927b-47a5-b193-04786c5c20bc\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"0584d557-927b-47a5-b193-04786c5c20bc\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"0584d557-927b-47a5-b193-04786c5c20bc\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"0584d557-927b-47a5-b193-04786c5c20bc\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [Prospect match] cached empty result - no API calls, try next matching method {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"0584d557-927b-47a5-b193-04786c5c20bc\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [Prospect match] Resolved company domain from email {\"email\":\"support@staging.jiminny.com\",\"domain\":\"jiminny.com\"} {\"correlation_id\":\"0584d557-927b-47a5-b193-04786c5c20bc\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":610462,\"team_id\":2,\"email\":\"veselin.kulov@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"0584d557-927b-47a5-b193-04786c5c20bc\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":610462,\"participants_processed\":2,\"exact_matches\":0,\"domain_matches\":1,\"best_match_found\":true} {\"correlation_id\":\"0584d557-927b-47a5-b193-04786c5c20bc\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610462} {\"correlation_id\":\"0584d557-927b-47a5-b193-04786c5c20bc\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610462,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"0584d557-927b-47a5-b193-04786c5c20bc\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":610462,\"remote_search\":true,\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89} {\"correlation_id\":\"0584d557-927b-47a5-b193-04786c5c20bc\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":610451,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89}} {\"correlation_id\":\"e86be547-45b5-48da-b389-b64c7928f856\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610451} {\"correlation_id\":\"e86be547-45b5-48da-b389-b64c7928f856\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610451,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"e86be547-45b5-48da-b389-b64c7928f856\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":610451,\"participants\":[{\"id\":996340,\"user_id\":null,\"contact_id\":null,\"lead_id\":null},{\"id\":996341,\"user_id\":18,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"e86be547-45b5-48da-b389-b64c7928f856\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"e86be547-45b5-48da-b389-b64c7928f856\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"e86be547-45b5-48da-b389-b64c7928f856\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"e86be547-45b5-48da-b389-b64c7928f856\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"e86be547-45b5-48da-b389-b64c7928f856\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"e86be547-45b5-48da-b389-b64c7928f856\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [Prospect match] cached empty result - no API calls, try next matching method {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"e86be547-45b5-48da-b389-b64c7928f856\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [Prospect match] Resolved company domain from email {\"email\":\"support@staging.jiminny.com\",\"domain\":\"jiminny.com\"} {\"correlation_id\":\"e86be547-45b5-48da-b389-b64c7928f856\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":610451,\"team_id\":2,\"email\":\"veselin.kulov@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"e86be547-45b5-48da-b389-b64c7928f856\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":610451,\"participants_processed\":2,\"exact_matches\":0,\"domain_matches\":1,\"best_match_found\":true} {\"correlation_id\":\"e86be547-45b5-48da-b389-b64c7928f856\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610451} {\"correlation_id\":\"e86be547-45b5-48da-b389-b64c7928f856\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610451,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"e86be547-45b5-48da-b389-b64c7928f856\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":610451,\"remote_search\":true,\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89} {\"correlation_id\":\"e86be547-45b5-48da-b389-b64c7928f856\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":610438,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89}} {\"correlation_id\":\"34e69f21-acca-487a-8565-fc42df81c0fe\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610438} {\"correlation_id\":\"34e69f21-acca-487a-8565-fc42df81c0fe\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610438,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"34e69f21-acca-487a-8565-fc42df81c0fe\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":610438,\"participants\":[{\"id\":996320,\"user_id\":null,\"contact_id\":null,\"lead_id\":null},{\"id\":996321,\"user_id\":18,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"34e69f21-acca-487a-8565-fc42df81c0fe\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"34e69f21-acca-487a-8565-fc42df81c0fe\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"34e69f21-acca-487a-8565-fc42df81c0fe\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"34e69f21-acca-487a-8565-fc42df81c0fe\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"34e69f21-acca-487a-8565-fc42df81c0fe\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"34e69f21-acca-487a-8565-fc42df81c0fe\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [Prospect match] cached empty result - no API calls, try next matching method {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"34e69f21-acca-487a-8565-fc42df81c0fe\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [Prospect match] Resolved company domain from email {\"email\":\"support@staging.jiminny.com\",\"domain\":\"jiminny.com\"} {\"correlation_id\":\"34e69f21-acca-487a-8565-fc42df81c0fe\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":610438,\"team_id\":2,\"email\":\"veselin.kulov@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"34e69f21-acca-487a-8565-fc42df81c0fe\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":610438,\"participants_processed\":2,\"exact_matches\":0,\"domain_matches\":1,\"best_match_found\":true} {\"correlation_id\":\"34e69f21-acca-487a-8565-fc42df81c0fe\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610438} {\"correlation_id\":\"34e69f21-acca-487a-8565-fc42df81c0fe\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610438,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"34e69f21-acca-487a-8565-fc42df81c0fe\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":610438,\"remote_search\":true,\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89} {\"correlation_id\":\"34e69f21-acca-487a-8565-fc42df81c0fe\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":610426,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89}} {\"correlation_id\":\"813c1b3a-95b1-4314-b468-5f42e908f99e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610426} {\"correlation_id\":\"813c1b3a-95b1-4314-b468-5f42e908f99e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610426,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"813c1b3a-95b1-4314-b468-5f42e908f99e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":610426,\"participants\":[{\"id\":996306,\"user_id\":null,\"contact_id\":null,\"lead_id\":null},{\"id\":996307,\"user_id\":18,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"813c1b3a-95b1-4314-b468-5f42e908f99e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"813c1b3a-95b1-4314-b468-5f42e908f99e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"813c1b3a-95b1-4314-b468-5f42e908f99e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"813c1b3a-95b1-4314-b468-5f42e908f99e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"813c1b3a-95b1-4314-b468-5f42e908f99e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"813c1b3a-95b1-4314-b468-5f42e908f99e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [Prospect match] cached empty result - no API calls, try next matching method {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"813c1b3a-95b1-4314-b468-5f42e908f99e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [Prospect match] Resolved company domain from email {\"email\":\"support@staging.jiminny.com\",\"domain\":\"jiminny.com\"} {\"correlation_id\":\"813c1b3a-95b1-4314-b468-5f42e908f99e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":610426,\"team_id\":2,\"email\":\"veselin.kulov@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"813c1b3a-95b1-4314-b468-5f42e908f99e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":610426,\"participants_processed\":2,\"exact_matches\":0,\"domain_matches\":1,\"best_match_found\":true} {\"correlation_id\":\"813c1b3a-95b1-4314-b468-5f42e908f99e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610426} {\"correlation_id\":\"813c1b3a-95b1-4314-b468-5f42e908f99e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610426,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"813c1b3a-95b1-4314-b468-5f42e908f99e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":610426,\"remote_search\":true,\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89} {\"correlation_id\":\"813c1b3a-95b1-4314-b468-5f42e908f99e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":610403,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89}} {\"correlation_id\":\"98026431-b755-4e7e-af17-86d8e2993031\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610403} {\"correlation_id\":\"98026431-b755-4e7e-af17-86d8e2993031\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610403,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"98026431-b755-4e7e-af17-86d8e2993031\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":610403,\"participants\":[{\"id\":996282,\"user_id\":null,\"contact_id\":null,\"lead_id\":null},{\"id\":996283,\"user_id\":18,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"98026431-b755-4e7e-af17-86d8e2993031\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"98026431-b755-4e7e-af17-86d8e2993031\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"98026431-b755-4e7e-af17-86d8e2993031\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"98026431-b755-4e7e-af17-86d8e2993031\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"98026431-b755-4e7e-af17-86d8e2993031\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"98026431-b755-4e7e-af17-86d8e2993031\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [Prospect match] cached empty result - no API calls, try next matching method {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"98026431-b755-4e7e-af17-86d8e2993031\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [Prospect match] Resolved company domain from email {\"email\":\"support@staging.jiminny.com\",\"domain\":\"jiminny.com\"} {\"correlation_id\":\"98026431-b755-4e7e-af17-86d8e2993031\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":610403,\"team_id\":2,\"email\":\"veselin.kulov@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"98026431-b755-4e7e-af17-86d8e2993031\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":610403,\"participants_processed\":2,\"exact_matches\":0,\"domain_matches\":1,\"best_match_found\":true} {\"correlation_id\":\"98026431-b755-4e7e-af17-86d8e2993031\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610403} {\"correlation_id\":\"98026431-b755-4e7e-af17-86d8e2993031\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610403,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"98026431-b755-4e7e-af17-86d8e2993031\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":610403,\"remote_search\":true,\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89} {\"correlation_id\":\"98026431-b755-4e7e-af17-86d8e2993031\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":610400,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":4487,\"account_id\":244,\"opportunity_id\":350,\"stage_id\":34}} {\"correlation_id\":\"4c991921-b335-438b-a656-aac035483aa5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610400} {\"correlation_id\":\"4c991921-b335-438b-a656-aac035483aa5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610400,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"4c991921-b335-438b-a656-aac035483aa5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":610400,\"participants\":[{\"id\":996275,\"user_id\":1460,\"contact_id\":null,\"lead_id\":null},{\"id\":996276,\"user_id\":206,\"contact_id\":null,\"lead_id\":null},{\"id\":996277,\"user_id\":null,\"contact_id\":4487,\"lead_id\":null}]} {\"correlation_id\":\"4c991921-b335-438b-a656-aac035483aa5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"4c991921-b335-438b-a656-aac035483aa5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"4c991921-b335-438b-a656-aac035483aa5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"4c991921-b335-438b-a656-aac035483aa5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"4c991921-b335-438b-a656-aac035483aa5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":610400,\"team_id\":2,\"email\":\"aneliya.angelova@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"4c991921-b335-438b-a656-aac035483aa5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":610400,\"team_id\":2,\"email\":\"horen.kirazyan@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"4c991921-b335-438b-a656-aac035483aa5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: ProspectCache - Searching DB for opportunity by owner {\"account_id\":244,\"contact_id\":4487,\"owner_id\":1460} {\"correlation_id\":\"4c991921-b335-438b-a656-aac035483aa5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: ProspectCache - Fallback DB opportunity search {\"account_id\":244,\"contact_id\":4487} {\"correlation_id\":\"4c991921-b335-438b-a656-aac035483aa5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: ProspectCache - Opportunity DB search results {\"account_id\":244,\"contact_id\":4487,\"opportunity_id\":350} {\"correlation_id\":\"4c991921-b335-438b-a656-aac035483aa5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"robinson@crusoe.com\"} {\"correlation_id\":\"4c991921-b335-438b-a656-aac035483aa5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":610400,\"participants_processed\":3,\"exact_matches\":1,\"domain_matches\":0,\"best_match_found\":true} {\"correlation_id\":\"4c991921-b335-438b-a656-aac035483aa5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610400} {\"correlation_id\":\"4c991921-b335-438b-a656-aac035483aa5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610400,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"4c991921-b335-438b-a656-aac035483aa5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":610400,\"remote_search\":true,\"lead_id\":null,\"contact_id\":4487,\"account_id\":244,\"opportunity_id\":350,\"stage_id\":34} {\"correlation_id\":\"4c991921-b335-438b-a656-aac035483aa5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [ EsUpdateProcessManager ] Finished updating entities in ES {\"worker\":\"\",\"peak_memory\":\"99.73 MB\",\"elapsed_seconds\":0.14,\"update_target\":\"activities\",\"should_iterate_again\":false} {\"correlation_id\":\"38935ae1-fcb3-437e-9c9e-1b7415a0b7e1\",\"trace_id\":\"325a2f9b-9c66-4223-ac02-1a43723f4315\"}\n[2026-05-11 11:06:30] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"conference:pre-meeting-notification\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.727} {\"correlation_id\":\"4fa18424-38b1-4f8a-a2e1-b467db754dba\",\"trace_id\":\"0d2c5c78-f780-4c85-81ff-c26ad669374b\"}\n[2026-05-11 11:06:30] local.INFO: Running pre-meeting notification command {\"correlation_id\":\"4fa18424-38b1-4f8a-a2e1-b467db754dba\",\"trace_id\":\"0d2c5c78-f780-4c85-81ff-c26ad669374b\"}\n[2026-05-11 11:06:30] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage for command {\"command\":\"conference:pre-meeting-notification\",\"memoryBeforeCommandInMb\":60.0,\"memoryAfterCommandInMB\":60.0,\"memoryPeakBeforeCommandInMb\":99.727,\"memoryPeakAfterCommandInMB\":99.727} {\"correlation_id\":\"4fa18424-38b1-4f8a-a2e1-b467db754dba\",\"trace_id\":\"0d2c5c78-f780-4c85-81ff-c26ad669374b\"}\n[2026-05-11 11:06:31] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"conference:monitor:start\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.727} {\"correlation_id\":\"ad10418d-4623-4ff3-b748-2e19330019b3\",\"trace_id\":\"a31c4682-7665-42af-99bd-8ab71b6cd9ac\"}\n[2026-05-11 11:06:31] local.INFO: Running conference:monitor:start command for activities in (2026-05-11 10:56:00, 2026-05-11 11:01:00] {\"correlation_id\":\"ad10418d-4623-4ff3-b748-2e19330019b3\",\"trace_id\":\"a31c4682-7665-42af-99bd-8ab71b6cd9ac\"}\n[2026-05-11 11:06:31] local.INFO: [conference:monitor:start] start ok {\"activity_id\":484852} {\"correlation_id\":\"ad10418d-4623-4ff3-b748-2e19330019b3\",\"trace_id\":\"a31c4682-7665-42af-99bd-8ab71b6cd9ac\"}\n[2026-05-11 11:06:31] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage for command {\"command\":\"conference:monitor:start\",\"memoryBeforeCommandInMb\":60.0,\"memoryAfterCommandInMB\":60.0,\"memoryPeakBeforeCommandInMb\":99.727,\"memoryPeakAfterCommandInMB\":99.727} {\"correlation_id\":\"ad10418d-4623-4ff3-b748-2e19330019b3\",\"trace_id\":\"a31c4682-7665-42af-99bd-8ab71b6cd9ac\"}\n[2026-05-11 11:06:33] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"conference:monitor:end\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.727} {\"correlation_id\":\"31a09cc3-a94d-4145-ab23-b041b077106a\",\"trace_id\":\"82ffd281-4725-402c-90c7-162549d4415a\"}\n[2026-05-11 11:06:33] local.INFO: conference:monitor:end:Jiminny\\Console\\Commands\\Activities\\MonitorMeetingEndCommand::logActivitiesEnded {\"from\":\"11:01\",\"to\":\"11:06\"} {\"correlation_id\":\"31a09cc3-a94d-4145-ab23-b041b077106a\",\"trace_id\":\"82ffd281-4725-402c-90c7-162549d4415a\"}\n[2026-05-11 11:06:33] local.INFO: conference:monitor:end:Jiminny\\Console\\Commands\\Activities\\MonitorMeetingEndCommand::logActivitiesWithUnfinishedSession {\"from\":\"00:56\",\"to\":\"01:01\"} {\"correlation_id\":\"31a09cc3-a94d-4145-ab23-b041b077106a\",\"trace_id\":\"82ffd281-4725-402c-90c7-162549d4415a\"}\n[2026-05-11 11:06:33] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage for command {\"command\":\"conference:monitor:end\",\"memoryBeforeCommandInMb\":60.0,\"memoryAfterCommandInMB\":60.0,\"memoryPeakBeforeCommandInMb\":99.727,\"memoryPeakAfterCommandInMB\":99.727} {\"correlation_id\":\"31a09cc3-a94d-4145-ab23-b041b077106a\",\"trace_id\":\"82ffd281-4725-402c-90c7-162549d4415a\"}\n[2026-05-11 11:06:35] local.NOTICE: Repairing HubSpot tokens start {\"correlation_id\":\"b78a7ca3-5d72-4b23-8794-4543dd6d8ffc\",\"trace_id\":\"bedc11e2-b932-44b3-a16d-436f13a1254e\"}\n[2026-05-11 11:06:35] local.INFO: Trying to refresh HubSpot token {\"account_id\":59,\"updated_at\":\"2025-10-03 09:32:05\"} {\"correlation_id\":\"b78a7ca3-5d72-4b23-8794-4543dd6d8ffc\",\"trace_id\":\"bedc11e2-b932-44b3-a16d-436f13a1254e\"}\n[2026-05-11 11:06:35] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"b78a7ca3-5d72-4b23-8794-4543dd6d8ffc\",\"trace_id\":\"bedc11e2-b932-44b3-a16d-436f13a1254e\"}\n[2026-05-11 11:06:35] local.INFO: [SocialAccountService] Refreshing token from provider {\"socialAccountId\":59,\"provider\":\"hubspot\",\"refreshToken\":\"97b78f6e2cc49965c00c2492b602b02708b1392551e6b3f113fbaa48992af90b\",\"state\":\"full-refresh\"} {\"correlation_id\":\"b78a7ca3-5d72-4b23-8794-4543dd6d8ffc\",\"trace_id\":\"bedc11e2-b932-44b3-a16d-436f13a1254e\"}\n[2026-05-11 11:06:35] local.ERROR: Failed to refresh HubSpot token {\"account_id\":59,\"updated_at\":\"2025-10-03 09:32:05\",\"reason\":\"missing or invalid refresh token\",\"previous\":\"\"} {\"correlation_id\":\"b78a7ca3-5d72-4b23-8794-4543dd6d8ffc\",\"trace_id\":\"bedc11e2-b932-44b3-a16d-436f13a1254e\"}\n[2026-05-11 11:06:35] local.INFO: Trying to refresh HubSpot token {\"account_id\":306,\"updated_at\":\"2023-11-27 09:30:03\"} {\"correlation_id\":\"b78a7ca3-5d72-4b23-8794-4543dd6d8ffc\",\"trace_id\":\"bedc11e2-b932-44b3-a16d-436f13a1254e\"}\n[2026-05-11 11:06:35] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"b78a7ca3-5d72-4b23-8794-4543dd6d8ffc\",\"trace_id\":\"bedc11e2-b932-44b3-a16d-436f13a1254e\"}\n[2026-05-11 11:06:35] local.INFO: [SocialAccountService] Refreshing token from provider {\"socialAccountId\":306,\"provider\":\"hubspot\",\"refreshToken\":\"6fa6aa8cc641d131231acc3470f5c03cb3b07b2e580fb18f8acb3b1dbb72549b\",\"state\":\"full-refresh\"} {\"correlation_id\":\"b78a7ca3-5d72-4b23-8794-4543dd6d8ffc\",\"trace_id\":\"bedc11e2-b932-44b3-a16d-436f13a1254e\"}\n[2026-05-11 11:06:36] local.ERROR: Failed to refresh HubSpot token {\"account_id\":306,\"updated_at\":\"2023-11-27 09:30:03\",\"reason\":\"missing or invalid refresh token\",\"previous\":\"\"} {\"correlation_id\":\"b78a7ca3-5d72-4b23-8794-4543dd6d8ffc\",\"trace_id\":\"bedc11e2-b932-44b3-a16d-436f13a1254e\"}\n[2026-05-11 11:06:36] local.INFO: Trying to refresh HubSpot token {\"account_id\":1372,\"updated_at\":\"2025-10-02 14:47:06\"} {\"correlation_id\":\"b78a7ca3-5d72-4b23-8794-4543dd6d8ffc\",\"trace_id\":\"bedc11e2-b932-44b3-a16d-436f13a1254e\"}\n[2026-05-11 11:06:36] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"b78a7ca3-5d72-4b23-8794-4543dd6d8ffc\",\"trace_id\":\"bedc11e2-b932-44b3-a16d-436f13a1254e\"}\n[2026-05-11 11:06:36] local.INFO: [SocialAccountService] Refreshing token from provider {\"socialAccountId\":1372,\"provider\":\"hubspot\",\"refreshToken\":\"9aa73948c761da29dce46c177cf9aee1fde483a44169ca38723f9f0597d7a8c4\",\"state\":\"full-refresh\"} {\"correlation_id\":\"b78a7ca3-5d72-4b23-8794-4543dd6d8ffc\",\"trace_id\":\"bedc11e2-b932-44b3-a16d-436f13a1254e\"}\n[2026-05-11 11:06:36] local.ERROR: Failed to refresh HubSpot token {\"account_id\":1372,\"updated_at\":\"2025-10-02 14:47:06\",\"reason\":\"missing or invalid refresh token\",\"previous\":\"\"} {\"correlation_id\":\"b78a7ca3-5d72-4b23-8794-4543dd6d8ffc\",\"trace_id\":\"bedc11e2-b932-44b3-a16d-436f13a1254e\"}\n[2026-05-11 11:06:36] local.NOTICE: Repairing HubSpot tokens end {\"total\":3,\"fixed\":0,\"failed\":3} {\"correlation_id\":\"b78a7ca3-5d72-4b23-8794-4543dd6d8ffc\",\"trace_id\":\"bedc11e2-b932-44b3-a16d-436f13a1254e\"}\n[2026-05-11 11:06:40] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"crm:bullhorn:ping\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.727} {\"correlation_id\":\"7f93fd49-8b24-4f96-a532-a4777ccf3183\",\"trace_id\":\"14797017-4a40-4a20-a018-dfa389b58256\"}\n[2026-05-11 11:06:40] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"conference:pre-meeting-reminder\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.727} {\"correlation_id\":\"00c329ca-e23d-4d27-974c-c9cce4940adf\",\"trace_id\":\"d29b535e-a485-4bb8-bcf4-61cc79b73da5\"}\n[2026-05-11 11:06:40] local.INFO: [HubSpot Journal Command] Starting polling service {\"correlation_id\":\"32a337c8-7047-40f4-b1f4-d3008f51e4da\",\"trace_id\":\"b65ab282-ade3-48d8-afd0-798307edc610\"}\n[2026-05-11 11:06:40] local.INFO: [HubSpot Journal Polling] Service starting {\"memory_limit\":\"256M\",\"max_execution_time\":\"0\",\"initial_memory_mb\":60.0} {\"correlation_id\":\"32a337c8-7047-40f4-b1f4-d3008f51e4da\",\"trace_id\":\"b65ab282-ade3-48d8-afd0-798307edc610\"}\n[2026-05-11 11:06:40] local.INFO: [HubSpot Journal Polling] Acquired polling lock {\"expires_at\":\"2026-05-11T11:08:40.813273Z\"} {\"correlation_id\":\"32a337c8-7047-40f4-b1f4-d3008f51e4da\",\"trace_id\":\"b65ab282-ade3-48d8-afd0-798307edc610\"}\n[2026-05-11 11:06:40] local.INFO: [HubSpot Journal Auth] Requesting new client credentials token {\"correlation_id\":\"32a337c8-7047-40f4-b1f4-d3008f51e4da\",\"trace_id\":\"b65ab282-ade3-48d8-afd0-798307edc610\"}\n[2026-05-11 11:06:40] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage for command {\"command\":\"crm:bullhorn:ping\",\"memoryBeforeCommandInMb\":60.0,\"memoryAfterCommandInMB\":60.0,\"memoryPeakBeforeCommandInMb\":99.727,\"memoryPeakAfterCommandInMB\":99.727} {\"correlation_id\":\"7f93fd49-8b24-4f96-a532-a4777ccf3183\",\"trace_id\":\"14797017-4a40-4a20-a018-dfa389b58256\"}\n[2026-05-11 11:06:40] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage for command {\"command\":\"conference:pre-meeting-reminder\",\"memoryBeforeCommandInMb\":60.0,\"memoryAfterCommandInMB\":60.0,\"memoryPeakBeforeCommandInMb\":99.727,\"memoryPeakAfterCommandInMB\":99.727} {\"correlation_id\":\"00c329ca-e23d-4d27-974c-c9cce4940adf\",\"trace_id\":\"d29b535e-a485-4bb8-bcf4-61cc79b73da5\"}\n[2026-05-11 11:06:41] local.INFO: [HubSpot Journal Auth] Successfully obtained new access token {\"expires_in\":1800,\"cached_for\":1500} {\"correlation_id\":\"32a337c8-7047-40f4-b1f4-d3008f51e4da\",\"trace_id\":\"b65ab282-ade3-48d8-afd0-798307edc610\"}\n[2026-05-11 11:06:41] local.INFO: [HubSpot Journal Polling] No data {\"correlation_id\":\"32a337c8-7047-40f4-b1f4-d3008f51e4da\",\"trace_id\":\"b65ab282-ade3-48d8-afd0-798307edc610\"}\n[2026-05-11 11:06:46] local.INFO: [HubSpot Journal Polling] No data {\"correlation_id\":\"32a337c8-7047-40f4-b1f4-d3008f51e4da\",\"trace_id\":\"b65ab282-ade3-48d8-afd0-798307edc610\"}","depth":4,"on_screen":true,"value":"[2026-05-11 11:06:08] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":612562,\"team_id\":2,\"email\":\"horen.kirazyan@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"c1949c5e-c0f4-431e-ba26-59d49408d871\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:08] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":612562,\"participants_processed\":2,\"exact_matches\":0,\"domain_matches\":1,\"best_match_found\":true} {\"correlation_id\":\"c1949c5e-c0f4-431e-ba26-59d49408d871\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:08] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612562} {\"correlation_id\":\"c1949c5e-c0f4-431e-ba26-59d49408d871\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:08] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612562,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"c1949c5e-c0f4-431e-ba26-59d49408d871\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:08] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":612562,\"remote_search\":true,\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89} {\"correlation_id\":\"c1949c5e-c0f4-431e-ba26-59d49408d871\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:09] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":612561,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89}} {\"correlation_id\":\"b715f531-04da-410f-be31-055aa1c13754\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:09] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612561} {\"correlation_id\":\"b715f531-04da-410f-be31-055aa1c13754\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:09] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612561,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"b715f531-04da-410f-be31-055aa1c13754\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:09] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":612561,\"participants\":[{\"id\":999780,\"user_id\":206,\"contact_id\":null,\"lead_id\":null},{\"id\":999781,\"user_id\":null,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"b715f531-04da-410f-be31-055aa1c13754\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:09] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"b715f531-04da-410f-be31-055aa1c13754\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:09] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"b715f531-04da-410f-be31-055aa1c13754\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:09] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"b715f531-04da-410f-be31-055aa1c13754\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:09] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"b715f531-04da-410f-be31-055aa1c13754\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:09] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":612561,\"team_id\":2,\"email\":\"horen.kirazyan@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"b715f531-04da-410f-be31-055aa1c13754\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:09] local.INFO: [Prospect match] Cache miss, calling the API {\"identifier_type\":\"email\",\"identifier\":\"447700174614.447782589921.OeREojLVnk@txt.staging.jiminny.com\"} {\"correlation_id\":\"b715f531-04da-410f-be31-055aa1c13754\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:10] local.INFO: [Hubspot] Failed to fetch contact {\"email\":\"447700174614.447782589921.OeREojLVnk@txt.staging.jiminny.com\",\"reason\":\"[404] Client error: `GET https://api.hubapi.com/crm/v3/objects/contacts/447700174614.447782589921.OeREojLVnk%40txt.staging.jiminny.com?properties=email%2Cfirstname%2Clastname%2Ccountry%2Cphone%2Cmobilephone%2Cjobtitle%2Chubspot_owner_id%2Cassociatedcompanyid%2Cphoto&archived=0&idProperty=email` resulted in a `404 Not Found` response\"} {\"correlation_id\":\"b715f531-04da-410f-be31-055aa1c13754\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:10] local.INFO: [Prospect match] API returned empty result, caching the miss with empty prospect data {\"identifier_type\":\"email\",\"identifier\":\"447700174614.447782589921.OeREojLVnk@txt.staging.jiminny.com\"} {\"correlation_id\":\"b715f531-04da-410f-be31-055aa1c13754\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:10] local.INFO: [Prospect match] Resolved company domain from email {\"email\":\"447700174614.447782589921.OeREojLVnk@txt.staging.jiminny.com\",\"domain\":\"jiminny.com\"} {\"correlation_id\":\"b715f531-04da-410f-be31-055aa1c13754\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:10] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":612561,\"participants_processed\":2,\"exact_matches\":0,\"domain_matches\":1,\"best_match_found\":true} {\"correlation_id\":\"b715f531-04da-410f-be31-055aa1c13754\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:10] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612561} {\"correlation_id\":\"b715f531-04da-410f-be31-055aa1c13754\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:10] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612561,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"b715f531-04da-410f-be31-055aa1c13754\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:10] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":612561,\"remote_search\":true,\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89} {\"correlation_id\":\"b715f531-04da-410f-be31-055aa1c13754\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:10] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":612560,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89}} {\"correlation_id\":\"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:10] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612560} {\"correlation_id\":\"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:10] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612560,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:10] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":612560,\"participants\":[{\"id\":999778,\"user_id\":null,\"contact_id\":null,\"lead_id\":null},{\"id\":999779,\"user_id\":206,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"447782589921@txt.staging.jiminny.com\"} {\"correlation_id\":\"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [Prospect match] cached empty result - no API calls, try next matching method {\"identifier_type\":\"email\",\"identifier\":\"447782589921@txt.staging.jiminny.com\"} {\"correlation_id\":\"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [Prospect match] Resolved company domain from email {\"email\":\"447782589921@txt.staging.jiminny.com\",\"domain\":\"jiminny.com\"} {\"correlation_id\":\"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":612560,\"team_id\":2,\"email\":\"horen.kirazyan@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":612560,\"participants_processed\":2,\"exact_matches\":0,\"domain_matches\":1,\"best_match_found\":true} {\"correlation_id\":\"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612560} {\"correlation_id\":\"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [ EsUpdateProcessManager ] Finished updating entities in ES {\"worker\":\"\",\"peak_memory\":\"99.73 MB\",\"elapsed_seconds\":0.74,\"update_target\":\"activities\",\"should_iterate_again\":false} {\"correlation_id\":\"38935ae1-fcb3-437e-9c9e-1b7415a0b7e1\",\"trace_id\":\"325a2f9b-9c66-4223-ac02-1a43723f4315\"}\n[2026-05-11 11:06:11] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612560,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":612560,\"remote_search\":true,\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89} {\"correlation_id\":\"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":612559,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":97,\"account_id\":69,\"opportunity_id\":5011,\"stage_id\":34}} {\"correlation_id\":\"4d59a59c-38a5-4a1d-9379-6ceaf67b747e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612559} {\"correlation_id\":\"4d59a59c-38a5-4a1d-9379-6ceaf67b747e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612559,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"4d59a59c-38a5-4a1d-9379-6ceaf67b747e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":612559,\"participants\":[{\"id\":999776,\"user_id\":206,\"contact_id\":null,\"lead_id\":null},{\"id\":999777,\"user_id\":null,\"contact_id\":97,\"lead_id\":null}]} {\"correlation_id\":\"4d59a59c-38a5-4a1d-9379-6ceaf67b747e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"4d59a59c-38a5-4a1d-9379-6ceaf67b747e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"4d59a59c-38a5-4a1d-9379-6ceaf67b747e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"4d59a59c-38a5-4a1d-9379-6ceaf67b747e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"4d59a59c-38a5-4a1d-9379-6ceaf67b747e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":612559,\"team_id\":2,\"email\":\"horen.kirazyan@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"4d59a59c-38a5-4a1d-9379-6ceaf67b747e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: ProspectCache - Searching DB for opportunity by owner {\"account_id\":69,\"contact_id\":97,\"owner_id\":206} {\"correlation_id\":\"4d59a59c-38a5-4a1d-9379-6ceaf67b747e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: ProspectCache - Fallback DB opportunity search {\"account_id\":69,\"contact_id\":97} {\"correlation_id\":\"4d59a59c-38a5-4a1d-9379-6ceaf67b747e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: ProspectCache - Opportunity DB search results {\"account_id\":69,\"contact_id\":97,\"opportunity_id\":5011} {\"correlation_id\":\"4d59a59c-38a5-4a1d-9379-6ceaf67b747e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"robinsoncrusoe@test.com\"} {\"correlation_id\":\"4d59a59c-38a5-4a1d-9379-6ceaf67b747e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":612559,\"participants_processed\":2,\"exact_matches\":1,\"domain_matches\":0,\"best_match_found\":true} {\"correlation_id\":\"4d59a59c-38a5-4a1d-9379-6ceaf67b747e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612559} {\"correlation_id\":\"4d59a59c-38a5-4a1d-9379-6ceaf67b747e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612559,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"4d59a59c-38a5-4a1d-9379-6ceaf67b747e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:11] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":612559,\"remote_search\":true,\"lead_id\":null,\"contact_id\":97,\"account_id\":69,\"opportunity_id\":5011,\"stage_id\":34} {\"correlation_id\":\"4d59a59c-38a5-4a1d-9379-6ceaf67b747e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:12] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":612558,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":97,\"account_id\":69,\"opportunity_id\":5011,\"stage_id\":34}} {\"correlation_id\":\"a105800d-d2f7-4772-a534-7791e262461c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:12] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612558} {\"correlation_id\":\"a105800d-d2f7-4772-a534-7791e262461c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:12] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612558,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"a105800d-d2f7-4772-a534-7791e262461c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:12] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":612558,\"participants\":[{\"id\":999774,\"user_id\":206,\"contact_id\":null,\"lead_id\":null},{\"id\":999775,\"user_id\":null,\"contact_id\":97,\"lead_id\":null}]} {\"correlation_id\":\"a105800d-d2f7-4772-a534-7791e262461c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:12] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"a105800d-d2f7-4772-a534-7791e262461c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:12] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"a105800d-d2f7-4772-a534-7791e262461c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:12] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"a105800d-d2f7-4772-a534-7791e262461c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:12] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"a105800d-d2f7-4772-a534-7791e262461c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:12] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":612558,\"team_id\":2,\"email\":\"horen.kirazyan@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"a105800d-d2f7-4772-a534-7791e262461c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:12] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"robinsoncrusoe@test.com\"} {\"correlation_id\":\"a105800d-d2f7-4772-a534-7791e262461c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:12] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":612558,\"participants_processed\":2,\"exact_matches\":1,\"domain_matches\":0,\"best_match_found\":true} {\"correlation_id\":\"a105800d-d2f7-4772-a534-7791e262461c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:12] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612558} {\"correlation_id\":\"a105800d-d2f7-4772-a534-7791e262461c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:12] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612558,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"a105800d-d2f7-4772-a534-7791e262461c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:12] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":612558,\"remote_search\":true,\"lead_id\":null,\"contact_id\":97,\"account_id\":69,\"opportunity_id\":5011,\"stage_id\":34} {\"correlation_id\":\"a105800d-d2f7-4772-a534-7791e262461c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:13] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":612557,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":97,\"account_id\":69,\"opportunity_id\":5011,\"stage_id\":34}} {\"correlation_id\":\"3ec57437-5796-4e44-8bf0-aaec149ac813\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:13] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612557} {\"correlation_id\":\"3ec57437-5796-4e44-8bf0-aaec149ac813\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:13] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612557,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"3ec57437-5796-4e44-8bf0-aaec149ac813\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:13] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":612557,\"participants\":[{\"id\":999772,\"user_id\":206,\"contact_id\":null,\"lead_id\":null},{\"id\":999773,\"user_id\":null,\"contact_id\":97,\"lead_id\":null}]} {\"correlation_id\":\"3ec57437-5796-4e44-8bf0-aaec149ac813\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:13] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"mailbox:skip-lists:refresh\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.727} {\"correlation_id\":\"5203040f-23be-43e6-8410-ba49ee1b38b4\",\"trace_id\":\"e787953d-9b78-4b63-a987-4f93f6892984\"}\n[2026-05-11 11:06:13] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"3ec57437-5796-4e44-8bf0-aaec149ac813\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:13] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"3ec57437-5796-4e44-8bf0-aaec149ac813\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:13] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"3ec57437-5796-4e44-8bf0-aaec149ac813\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:13] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"3ec57437-5796-4e44-8bf0-aaec149ac813\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:13] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":612557,\"team_id\":2,\"email\":\"horen.kirazyan@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"3ec57437-5796-4e44-8bf0-aaec149ac813\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:13] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"robinsoncrusoe@test.com\"} {\"correlation_id\":\"3ec57437-5796-4e44-8bf0-aaec149ac813\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:13] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":612557,\"participants_processed\":2,\"exact_matches\":1,\"domain_matches\":0,\"best_match_found\":true} {\"correlation_id\":\"3ec57437-5796-4e44-8bf0-aaec149ac813\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:13] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612557} {\"correlation_id\":\"3ec57437-5796-4e44-8bf0-aaec149ac813\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:13] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612557,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"3ec57437-5796-4e44-8bf0-aaec149ac813\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:13] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":612557,\"remote_search\":true,\"lead_id\":null,\"contact_id\":97,\"account_id\":69,\"opportunity_id\":5011,\"stage_id\":34} {\"correlation_id\":\"3ec57437-5796-4e44-8bf0-aaec149ac813\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:13] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage for command {\"command\":\"mailbox:skip-lists:refresh\",\"memoryBeforeCommandInMb\":60.0,\"memoryAfterCommandInMB\":60.0,\"memoryPeakBeforeCommandInMb\":99.727,\"memoryPeakAfterCommandInMB\":99.727} {\"correlation_id\":\"5203040f-23be-43e6-8410-ba49ee1b38b4\",\"trace_id\":\"e787953d-9b78-4b63-a987-4f93f6892984\"}\n[2026-05-11 11:06:13] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":612556,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":97,\"account_id\":69,\"opportunity_id\":5011,\"stage_id\":34}} {\"correlation_id\":\"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:13] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612556} {\"correlation_id\":\"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:13] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612556,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:13] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":612556,\"participants\":[{\"id\":999770,\"user_id\":206,\"contact_id\":null,\"lead_id\":null},{\"id\":999771,\"user_id\":null,\"contact_id\":97,\"lead_id\":null}]} {\"correlation_id\":\"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:13] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:13] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:13] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:13] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":612556,\"team_id\":2,\"email\":\"horen.kirazyan@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"robinsoncrusoe@test.com\"} {\"correlation_id\":\"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":612556,\"participants_processed\":2,\"exact_matches\":1,\"domain_matches\":0,\"best_match_found\":true} {\"correlation_id\":\"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612556} {\"correlation_id\":\"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612556,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":612556,\"remote_search\":true,\"lead_id\":null,\"contact_id\":97,\"account_id\":69,\"opportunity_id\":5011,\"stage_id\":34} {\"correlation_id\":\"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":612555,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":97,\"account_id\":69,\"opportunity_id\":5011,\"stage_id\":34}} {\"correlation_id\":\"3e9d1a70-5491-44ae-8f28-5cb9b60e1859\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612555} {\"correlation_id\":\"3e9d1a70-5491-44ae-8f28-5cb9b60e1859\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612555,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"3e9d1a70-5491-44ae-8f28-5cb9b60e1859\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":612555,\"participants\":[{\"id\":999768,\"user_id\":206,\"contact_id\":null,\"lead_id\":null},{\"id\":999769,\"user_id\":null,\"contact_id\":97,\"lead_id\":null}]} {\"correlation_id\":\"3e9d1a70-5491-44ae-8f28-5cb9b60e1859\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"3e9d1a70-5491-44ae-8f28-5cb9b60e1859\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"3e9d1a70-5491-44ae-8f28-5cb9b60e1859\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"3e9d1a70-5491-44ae-8f28-5cb9b60e1859\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"3e9d1a70-5491-44ae-8f28-5cb9b60e1859\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":612555,\"team_id\":2,\"email\":\"horen.kirazyan@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"3e9d1a70-5491-44ae-8f28-5cb9b60e1859\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"robinsoncrusoe@test.com\"} {\"correlation_id\":\"3e9d1a70-5491-44ae-8f28-5cb9b60e1859\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":612555,\"participants_processed\":2,\"exact_matches\":1,\"domain_matches\":0,\"best_match_found\":true} {\"correlation_id\":\"3e9d1a70-5491-44ae-8f28-5cb9b60e1859\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612555} {\"correlation_id\":\"3e9d1a70-5491-44ae-8f28-5cb9b60e1859\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612555,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"3e9d1a70-5491-44ae-8f28-5cb9b60e1859\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":612555,\"remote_search\":true,\"lead_id\":null,\"contact_id\":97,\"account_id\":69,\"opportunity_id\":5011,\"stage_id\":34} {\"correlation_id\":\"3e9d1a70-5491-44ae-8f28-5cb9b60e1859\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":612554,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":97,\"account_id\":69,\"opportunity_id\":5011,\"stage_id\":34}} {\"correlation_id\":\"7101e979-3a7e-4b2c-bf04-dc6a24ba1356\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612554} {\"correlation_id\":\"7101e979-3a7e-4b2c-bf04-dc6a24ba1356\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612554,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"7101e979-3a7e-4b2c-bf04-dc6a24ba1356\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":612554,\"participants\":[{\"id\":999766,\"user_id\":206,\"contact_id\":null,\"lead_id\":null},{\"id\":999767,\"user_id\":null,\"contact_id\":97,\"lead_id\":null}]} {\"correlation_id\":\"7101e979-3a7e-4b2c-bf04-dc6a24ba1356\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"7101e979-3a7e-4b2c-bf04-dc6a24ba1356\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"7101e979-3a7e-4b2c-bf04-dc6a24ba1356\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"7101e979-3a7e-4b2c-bf04-dc6a24ba1356\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"7101e979-3a7e-4b2c-bf04-dc6a24ba1356\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":612554,\"team_id\":2,\"email\":\"horen.kirazyan@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"7101e979-3a7e-4b2c-bf04-dc6a24ba1356\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"robinsoncrusoe@test.com\"} {\"correlation_id\":\"7101e979-3a7e-4b2c-bf04-dc6a24ba1356\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":612554,\"participants_processed\":2,\"exact_matches\":1,\"domain_matches\":0,\"best_match_found\":true} {\"correlation_id\":\"7101e979-3a7e-4b2c-bf04-dc6a24ba1356\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612554} {\"correlation_id\":\"7101e979-3a7e-4b2c-bf04-dc6a24ba1356\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612554,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"7101e979-3a7e-4b2c-bf04-dc6a24ba1356\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":612554,\"remote_search\":true,\"lead_id\":null,\"contact_id\":97,\"account_id\":69,\"opportunity_id\":5011,\"stage_id\":34} {\"correlation_id\":\"7101e979-3a7e-4b2c-bf04-dc6a24ba1356\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":612553,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":97,\"account_id\":69,\"opportunity_id\":5011,\"stage_id\":34}} {\"correlation_id\":\"346dc229-757b-496c-aceb-d2ddb367c7d6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612553} {\"correlation_id\":\"346dc229-757b-496c-aceb-d2ddb367c7d6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612553,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"346dc229-757b-496c-aceb-d2ddb367c7d6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":612553,\"participants\":[{\"id\":999764,\"user_id\":206,\"contact_id\":null,\"lead_id\":null},{\"id\":999765,\"user_id\":null,\"contact_id\":97,\"lead_id\":null}]} {\"correlation_id\":\"346dc229-757b-496c-aceb-d2ddb367c7d6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"346dc229-757b-496c-aceb-d2ddb367c7d6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"346dc229-757b-496c-aceb-d2ddb367c7d6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"346dc229-757b-496c-aceb-d2ddb367c7d6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"346dc229-757b-496c-aceb-d2ddb367c7d6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":612553,\"team_id\":2,\"email\":\"horen.kirazyan@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"346dc229-757b-496c-aceb-d2ddb367c7d6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"robinsoncrusoe@test.com\"} {\"correlation_id\":\"346dc229-757b-496c-aceb-d2ddb367c7d6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":612553,\"participants_processed\":2,\"exact_matches\":1,\"domain_matches\":0,\"best_match_found\":true} {\"correlation_id\":\"346dc229-757b-496c-aceb-d2ddb367c7d6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612553} {\"correlation_id\":\"346dc229-757b-496c-aceb-d2ddb367c7d6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612553,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"346dc229-757b-496c-aceb-d2ddb367c7d6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":612553,\"remote_search\":true,\"lead_id\":null,\"contact_id\":97,\"account_id\":69,\"opportunity_id\":5011,\"stage_id\":34} {\"correlation_id\":\"346dc229-757b-496c-aceb-d2ddb367c7d6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":612552,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":97,\"account_id\":69,\"opportunity_id\":5011,\"stage_id\":34}} {\"correlation_id\":\"56ef8656-0bfa-4f11-a245-9254b8179555\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612552} {\"correlation_id\":\"56ef8656-0bfa-4f11-a245-9254b8179555\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612552,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"56ef8656-0bfa-4f11-a245-9254b8179555\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":612552,\"participants\":[{\"id\":999762,\"user_id\":206,\"contact_id\":null,\"lead_id\":null},{\"id\":999763,\"user_id\":null,\"contact_id\":97,\"lead_id\":null}]} {\"correlation_id\":\"56ef8656-0bfa-4f11-a245-9254b8179555\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"56ef8656-0bfa-4f11-a245-9254b8179555\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"56ef8656-0bfa-4f11-a245-9254b8179555\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"56ef8656-0bfa-4f11-a245-9254b8179555\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"56ef8656-0bfa-4f11-a245-9254b8179555\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":612552,\"team_id\":2,\"email\":\"horen.kirazyan@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"56ef8656-0bfa-4f11-a245-9254b8179555\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"robinsoncrusoe@test.com\"} {\"correlation_id\":\"56ef8656-0bfa-4f11-a245-9254b8179555\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":612552,\"participants_processed\":2,\"exact_matches\":1,\"domain_matches\":0,\"best_match_found\":true} {\"correlation_id\":\"56ef8656-0bfa-4f11-a245-9254b8179555\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612552} {\"correlation_id\":\"56ef8656-0bfa-4f11-a245-9254b8179555\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612552,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"56ef8656-0bfa-4f11-a245-9254b8179555\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:14] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":612552,\"remote_search\":true,\"lead_id\":null,\"contact_id\":97,\"account_id\":69,\"opportunity_id\":5011,\"stage_id\":34} {\"correlation_id\":\"56ef8656-0bfa-4f11-a245-9254b8179555\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":612551,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":97,\"account_id\":69,\"opportunity_id\":5011,\"stage_id\":34}} {\"correlation_id\":\"df5e35c3-b4ae-4eba-9108-cd30ac152335\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612551} {\"correlation_id\":\"df5e35c3-b4ae-4eba-9108-cd30ac152335\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612551,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"df5e35c3-b4ae-4eba-9108-cd30ac152335\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":612551,\"participants\":[{\"id\":999760,\"user_id\":206,\"contact_id\":null,\"lead_id\":null},{\"id\":999761,\"user_id\":null,\"contact_id\":97,\"lead_id\":null}]} {\"correlation_id\":\"df5e35c3-b4ae-4eba-9108-cd30ac152335\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"df5e35c3-b4ae-4eba-9108-cd30ac152335\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"df5e35c3-b4ae-4eba-9108-cd30ac152335\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"df5e35c3-b4ae-4eba-9108-cd30ac152335\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"df5e35c3-b4ae-4eba-9108-cd30ac152335\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":612551,\"team_id\":2,\"email\":\"horen.kirazyan@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"df5e35c3-b4ae-4eba-9108-cd30ac152335\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"robinsoncrusoe@test.com\"} {\"correlation_id\":\"df5e35c3-b4ae-4eba-9108-cd30ac152335\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":612551,\"participants_processed\":2,\"exact_matches\":1,\"domain_matches\":0,\"best_match_found\":true} {\"correlation_id\":\"df5e35c3-b4ae-4eba-9108-cd30ac152335\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612551} {\"correlation_id\":\"df5e35c3-b4ae-4eba-9108-cd30ac152335\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612551,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"df5e35c3-b4ae-4eba-9108-cd30ac152335\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":612551,\"remote_search\":true,\"lead_id\":null,\"contact_id\":97,\"account_id\":69,\"opportunity_id\":5011,\"stage_id\":34} {\"correlation_id\":\"df5e35c3-b4ae-4eba-9108-cd30ac152335\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":612550,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":97,\"account_id\":69,\"opportunity_id\":5011,\"stage_id\":34}} {\"correlation_id\":\"64768905-983e-4f4e-a368-901931486f34\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612550} {\"correlation_id\":\"64768905-983e-4f4e-a368-901931486f34\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612550,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"64768905-983e-4f4e-a368-901931486f34\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":612550,\"participants\":[{\"id\":999758,\"user_id\":206,\"contact_id\":null,\"lead_id\":null},{\"id\":999759,\"user_id\":null,\"contact_id\":97,\"lead_id\":null}]} {\"correlation_id\":\"64768905-983e-4f4e-a368-901931486f34\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"64768905-983e-4f4e-a368-901931486f34\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"64768905-983e-4f4e-a368-901931486f34\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"64768905-983e-4f4e-a368-901931486f34\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"64768905-983e-4f4e-a368-901931486f34\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":612550,\"team_id\":2,\"email\":\"horen.kirazyan@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"64768905-983e-4f4e-a368-901931486f34\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"robinsoncrusoe@test.com\"} {\"correlation_id\":\"64768905-983e-4f4e-a368-901931486f34\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":612550,\"participants_processed\":2,\"exact_matches\":1,\"domain_matches\":0,\"best_match_found\":true} {\"correlation_id\":\"64768905-983e-4f4e-a368-901931486f34\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612550} {\"correlation_id\":\"64768905-983e-4f4e-a368-901931486f34\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612550,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"64768905-983e-4f4e-a368-901931486f34\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":612550,\"remote_search\":true,\"lead_id\":null,\"contact_id\":97,\"account_id\":69,\"opportunity_id\":5011,\"stage_id\":34} {\"correlation_id\":\"64768905-983e-4f4e-a368-901931486f34\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":612549,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":97,\"account_id\":69,\"opportunity_id\":5011,\"stage_id\":34}} {\"correlation_id\":\"97c9609f-bef0-4e95-8301-9f5549d29c1e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612549} {\"correlation_id\":\"97c9609f-bef0-4e95-8301-9f5549d29c1e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612549,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"97c9609f-bef0-4e95-8301-9f5549d29c1e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":612549,\"participants\":[{\"id\":999756,\"user_id\":206,\"contact_id\":null,\"lead_id\":null},{\"id\":999757,\"user_id\":null,\"contact_id\":97,\"lead_id\":null}]} {\"correlation_id\":\"97c9609f-bef0-4e95-8301-9f5549d29c1e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"97c9609f-bef0-4e95-8301-9f5549d29c1e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"97c9609f-bef0-4e95-8301-9f5549d29c1e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"97c9609f-bef0-4e95-8301-9f5549d29c1e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"97c9609f-bef0-4e95-8301-9f5549d29c1e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":612549,\"team_id\":2,\"email\":\"horen.kirazyan@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"97c9609f-bef0-4e95-8301-9f5549d29c1e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"robinsoncrusoe@test.com\"} {\"correlation_id\":\"97c9609f-bef0-4e95-8301-9f5549d29c1e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":612549,\"participants_processed\":2,\"exact_matches\":1,\"domain_matches\":0,\"best_match_found\":true} {\"correlation_id\":\"97c9609f-bef0-4e95-8301-9f5549d29c1e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612549} {\"correlation_id\":\"97c9609f-bef0-4e95-8301-9f5549d29c1e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612549,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"97c9609f-bef0-4e95-8301-9f5549d29c1e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":612549,\"remote_search\":true,\"lead_id\":null,\"contact_id\":97,\"account_id\":69,\"opportunity_id\":5011,\"stage_id\":34} {\"correlation_id\":\"97c9609f-bef0-4e95-8301-9f5549d29c1e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":612365,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":4491,\"account_id\":243,\"opportunity_id\":276,\"stage_id\":36}} {\"correlation_id\":\"7761b4e0-c2d4-4805-8d1d-26728cbffb07\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612365} {\"correlation_id\":\"7761b4e0-c2d4-4805-8d1d-26728cbffb07\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612365,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"7761b4e0-c2d4-4805-8d1d-26728cbffb07\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":612365,\"participants\":[{\"id\":999563,\"user_id\":null,\"contact_id\":4491,\"lead_id\":null},{\"id\":999564,\"user_id\":206,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"7761b4e0-c2d4-4805-8d1d-26728cbffb07\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"7761b4e0-c2d4-4805-8d1d-26728cbffb07\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"7761b4e0-c2d4-4805-8d1d-26728cbffb07\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"7761b4e0-c2d4-4805-8d1d-26728cbffb07\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"7761b4e0-c2d4-4805-8d1d-26728cbffb07\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"horencho@gmail.com\"} {\"correlation_id\":\"7761b4e0-c2d4-4805-8d1d-26728cbffb07\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":612365,\"team_id\":2,\"email\":\"horen.kirazyan@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"7761b4e0-c2d4-4805-8d1d-26728cbffb07\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":612365,\"participants_processed\":2,\"exact_matches\":1,\"domain_matches\":0,\"best_match_found\":true} {\"correlation_id\":\"7761b4e0-c2d4-4805-8d1d-26728cbffb07\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612365} {\"correlation_id\":\"7761b4e0-c2d4-4805-8d1d-26728cbffb07\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612365,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"7761b4e0-c2d4-4805-8d1d-26728cbffb07\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":612365,\"remote_search\":true,\"lead_id\":null,\"contact_id\":4491,\"account_id\":243,\"opportunity_id\":276,\"stage_id\":36} {\"correlation_id\":\"7761b4e0-c2d4-4805-8d1d-26728cbffb07\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":612360,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":4491,\"account_id\":243,\"opportunity_id\":276,\"stage_id\":36}} {\"correlation_id\":\"b8b554cd-4830-4ac5-be75-595cc7eb6ca8\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612360} {\"correlation_id\":\"b8b554cd-4830-4ac5-be75-595cc7eb6ca8\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612360,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"b8b554cd-4830-4ac5-be75-595cc7eb6ca8\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":612360,\"participants\":[{\"id\":999552,\"user_id\":null,\"contact_id\":4491,\"lead_id\":null},{\"id\":999553,\"user_id\":206,\"contact_id\":null,\"lead_id\":null},{\"id\":999565,\"user_id\":null,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"b8b554cd-4830-4ac5-be75-595cc7eb6ca8\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"b8b554cd-4830-4ac5-be75-595cc7eb6ca8\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"b8b554cd-4830-4ac5-be75-595cc7eb6ca8\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"b8b554cd-4830-4ac5-be75-595cc7eb6ca8\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"b8b554cd-4830-4ac5-be75-595cc7eb6ca8\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"horencho@gmail.com\"} {\"correlation_id\":\"b8b554cd-4830-4ac5-be75-595cc7eb6ca8\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:15] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":612360,\"team_id\":2,\"email\":\"horen.kirazyan@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"b8b554cd-4830-4ac5-be75-595cc7eb6ca8\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [Hubspot] Pagination completed {\"team_id\":2,\"endpoint\":\"https://api.hubapi.com/crm/v3/objects/contact/search\",\"total_requests\":1,\"total_records_fetched\":0,\"total_elapsed_seconds\":0.31,\"average_seconds_per_request\":0.31} {\"correlation_id\":\"b8b554cd-4830-4ac5-be75-595cc7eb6ca8\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":612360,\"participants_processed\":3,\"exact_matches\":1,\"domain_matches\":0,\"best_match_found\":true} {\"correlation_id\":\"b8b554cd-4830-4ac5-be75-595cc7eb6ca8\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612360} {\"correlation_id\":\"b8b554cd-4830-4ac5-be75-595cc7eb6ca8\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612360,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"b8b554cd-4830-4ac5-be75-595cc7eb6ca8\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":612360,\"remote_search\":true,\"lead_id\":null,\"contact_id\":4491,\"account_id\":243,\"opportunity_id\":276,\"stage_id\":36} {\"correlation_id\":\"b8b554cd-4830-4ac5-be75-595cc7eb6ca8\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":612340,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":4491,\"account_id\":243,\"opportunity_id\":276,\"stage_id\":36}} {\"correlation_id\":\"9a47ab79-dbb3-46e2-ad27-3a5120c080ef\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612340} {\"correlation_id\":\"9a47ab79-dbb3-46e2-ad27-3a5120c080ef\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612340,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"9a47ab79-dbb3-46e2-ad27-3a5120c080ef\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":612340,\"participants\":[{\"id\":999516,\"user_id\":null,\"contact_id\":4491,\"lead_id\":null},{\"id\":999517,\"user_id\":206,\"contact_id\":null,\"lead_id\":null},{\"id\":999518,\"user_id\":null,\"contact_id\":null,\"lead_id\":null},{\"id\":999519,\"user_id\":null,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"9a47ab79-dbb3-46e2-ad27-3a5120c080ef\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"9a47ab79-dbb3-46e2-ad27-3a5120c080ef\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"9a47ab79-dbb3-46e2-ad27-3a5120c080ef\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"9a47ab79-dbb3-46e2-ad27-3a5120c080ef\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"9a47ab79-dbb3-46e2-ad27-3a5120c080ef\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"horencho@gmail.com\"} {\"correlation_id\":\"9a47ab79-dbb3-46e2-ad27-3a5120c080ef\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":612340,\"team_id\":2,\"email\":\"horen.kirazyan@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"9a47ab79-dbb3-46e2-ad27-3a5120c080ef\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [Hubspot] Pagination completed {\"team_id\":2,\"endpoint\":\"https://api.hubapi.com/crm/v3/objects/contact/search\",\"total_requests\":1,\"total_records_fetched\":0,\"total_elapsed_seconds\":0.24,\"average_seconds_per_request\":0.24} {\"correlation_id\":\"9a47ab79-dbb3-46e2-ad27-3a5120c080ef\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":612340,\"participants_processed\":4,\"exact_matches\":1,\"domain_matches\":0,\"best_match_found\":true} {\"correlation_id\":\"9a47ab79-dbb3-46e2-ad27-3a5120c080ef\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [ EsUpdateProcessManager ] Finished updating entities in ES {\"worker\":\"\",\"peak_memory\":\"99.73 MB\",\"elapsed_seconds\":0.45,\"update_target\":\"activities\",\"should_iterate_again\":false} {\"correlation_id\":\"38935ae1-fcb3-437e-9c9e-1b7415a0b7e1\",\"trace_id\":\"325a2f9b-9c66-4223-ac02-1a43723f4315\"}\n[2026-05-11 11:06:16] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612340} {\"correlation_id\":\"9a47ab79-dbb3-46e2-ad27-3a5120c080ef\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612340,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"9a47ab79-dbb3-46e2-ad27-3a5120c080ef\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":612340,\"remote_search\":true,\"lead_id\":null,\"contact_id\":4491,\"account_id\":243,\"opportunity_id\":276,\"stage_id\":36} {\"correlation_id\":\"9a47ab79-dbb3-46e2-ad27-3a5120c080ef\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":612339,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":4491,\"account_id\":243,\"opportunity_id\":276,\"stage_id\":36}} {\"correlation_id\":\"eae16400-35f9-4885-b5fc-edb8872350d4\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612339} {\"correlation_id\":\"eae16400-35f9-4885-b5fc-edb8872350d4\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612339,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"eae16400-35f9-4885-b5fc-edb8872350d4\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":612339,\"participants\":[{\"id\":999514,\"user_id\":null,\"contact_id\":4491,\"lead_id\":null},{\"id\":999515,\"user_id\":206,\"contact_id\":null,\"lead_id\":null},{\"id\":999540,\"user_id\":null,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"eae16400-35f9-4885-b5fc-edb8872350d4\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"eae16400-35f9-4885-b5fc-edb8872350d4\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"eae16400-35f9-4885-b5fc-edb8872350d4\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"eae16400-35f9-4885-b5fc-edb8872350d4\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"eae16400-35f9-4885-b5fc-edb8872350d4\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"horencho@gmail.com\"} {\"correlation_id\":\"eae16400-35f9-4885-b5fc-edb8872350d4\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":612339,\"team_id\":2,\"email\":\"horen.kirazyan@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"eae16400-35f9-4885-b5fc-edb8872350d4\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":612339,\"participants_processed\":3,\"exact_matches\":1,\"domain_matches\":0,\"best_match_found\":true} {\"correlation_id\":\"eae16400-35f9-4885-b5fc-edb8872350d4\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612339} {\"correlation_id\":\"eae16400-35f9-4885-b5fc-edb8872350d4\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612339,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"eae16400-35f9-4885-b5fc-edb8872350d4\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:16] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":612339,\"remote_search\":true,\"lead_id\":null,\"contact_id\":4491,\"account_id\":243,\"opportunity_id\":276,\"stage_id\":36} {\"correlation_id\":\"eae16400-35f9-4885-b5fc-edb8872350d4\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":612336,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":4491,\"account_id\":243,\"opportunity_id\":276,\"stage_id\":36}} {\"correlation_id\":\"1028998b-3369-4f2f-b7cb-e4ef79db71a5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612336} {\"correlation_id\":\"1028998b-3369-4f2f-b7cb-e4ef79db71a5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612336,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"1028998b-3369-4f2f-b7cb-e4ef79db71a5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":612336,\"participants\":[{\"id\":999508,\"user_id\":null,\"contact_id\":4491,\"lead_id\":null},{\"id\":999509,\"user_id\":206,\"contact_id\":null,\"lead_id\":null},{\"id\":999512,\"user_id\":null,\"contact_id\":null,\"lead_id\":null},{\"id\":999513,\"user_id\":null,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"1028998b-3369-4f2f-b7cb-e4ef79db71a5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"1028998b-3369-4f2f-b7cb-e4ef79db71a5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"1028998b-3369-4f2f-b7cb-e4ef79db71a5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"1028998b-3369-4f2f-b7cb-e4ef79db71a5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"1028998b-3369-4f2f-b7cb-e4ef79db71a5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"horencho@gmail.com\"} {\"correlation_id\":\"1028998b-3369-4f2f-b7cb-e4ef79db71a5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":612336,\"team_id\":2,\"email\":\"horen.kirazyan@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"1028998b-3369-4f2f-b7cb-e4ef79db71a5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":612336,\"participants_processed\":4,\"exact_matches\":1,\"domain_matches\":0,\"best_match_found\":true} {\"correlation_id\":\"1028998b-3369-4f2f-b7cb-e4ef79db71a5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612336} {\"correlation_id\":\"1028998b-3369-4f2f-b7cb-e4ef79db71a5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612336,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"1028998b-3369-4f2f-b7cb-e4ef79db71a5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":612336,\"remote_search\":true,\"lead_id\":null,\"contact_id\":4491,\"account_id\":243,\"opportunity_id\":276,\"stage_id\":36} {\"correlation_id\":\"1028998b-3369-4f2f-b7cb-e4ef79db71a5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":612183,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":4487,\"account_id\":244,\"opportunity_id\":299,\"stage_id\":36}} {\"correlation_id\":\"135ad495-9ac2-4ae2-9428-66f40f3a4731\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612183} {\"correlation_id\":\"135ad495-9ac2-4ae2-9428-66f40f3a4731\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612183,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"135ad495-9ac2-4ae2-9428-66f40f3a4731\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":612183,\"participants\":[{\"id\":999227,\"user_id\":null,\"contact_id\":4487,\"lead_id\":null},{\"id\":999228,\"user_id\":261,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"135ad495-9ac2-4ae2-9428-66f40f3a4731\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"135ad495-9ac2-4ae2-9428-66f40f3a4731\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"135ad495-9ac2-4ae2-9428-66f40f3a4731\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"135ad495-9ac2-4ae2-9428-66f40f3a4731\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"135ad495-9ac2-4ae2-9428-66f40f3a4731\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: ProspectCache - Searching DB for opportunity by owner {\"account_id\":244,\"contact_id\":4487,\"owner_id\":261} {\"correlation_id\":\"135ad495-9ac2-4ae2-9428-66f40f3a4731\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: ProspectCache - Opportunity DB search results {\"account_id\":244,\"contact_id\":4487,\"opportunity_id\":299} {\"correlation_id\":\"135ad495-9ac2-4ae2-9428-66f40f3a4731\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"robinson@crusoe.com\"} {\"correlation_id\":\"135ad495-9ac2-4ae2-9428-66f40f3a4731\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":612183,\"team_id\":2,\"email\":\"adelina.petrova@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"135ad495-9ac2-4ae2-9428-66f40f3a4731\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":612183,\"participants_processed\":2,\"exact_matches\":1,\"domain_matches\":0,\"best_match_found\":true} {\"correlation_id\":\"135ad495-9ac2-4ae2-9428-66f40f3a4731\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612183} {\"correlation_id\":\"135ad495-9ac2-4ae2-9428-66f40f3a4731\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612183,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"135ad495-9ac2-4ae2-9428-66f40f3a4731\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":612183,\"remote_search\":true,\"lead_id\":null,\"contact_id\":4487,\"account_id\":244,\"opportunity_id\":299,\"stage_id\":36} {\"correlation_id\":\"135ad495-9ac2-4ae2-9428-66f40f3a4731\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":612182,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":4487,\"account_id\":244,\"opportunity_id\":299,\"stage_id\":36}} {\"correlation_id\":\"9a2d51b0-64bc-41ea-86b1-8f3bcbb65a89\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612182} {\"correlation_id\":\"9a2d51b0-64bc-41ea-86b1-8f3bcbb65a89\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612182,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"9a2d51b0-64bc-41ea-86b1-8f3bcbb65a89\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":612182,\"participants\":[{\"id\":999225,\"user_id\":null,\"contact_id\":4487,\"lead_id\":null},{\"id\":999226,\"user_id\":261,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"9a2d51b0-64bc-41ea-86b1-8f3bcbb65a89\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"mailbox:batch:process\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.727} {\"correlation_id\":\"dceccba8-9e9c-476e-993d-809067884ebe\",\"trace_id\":\"66f8bba8-1712-464b-b25a-418f16736be2\"}\n[2026-05-11 11:06:17] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"9a2d51b0-64bc-41ea-86b1-8f3bcbb65a89\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"9a2d51b0-64bc-41ea-86b1-8f3bcbb65a89\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"9a2d51b0-64bc-41ea-86b1-8f3bcbb65a89\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"9a2d51b0-64bc-41ea-86b1-8f3bcbb65a89\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"robinson@crusoe.com\"} {\"correlation_id\":\"9a2d51b0-64bc-41ea-86b1-8f3bcbb65a89\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":612182,\"team_id\":2,\"email\":\"adelina.petrova@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"9a2d51b0-64bc-41ea-86b1-8f3bcbb65a89\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":612182,\"participants_processed\":2,\"exact_matches\":1,\"domain_matches\":0,\"best_match_found\":true} {\"correlation_id\":\"9a2d51b0-64bc-41ea-86b1-8f3bcbb65a89\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612182} {\"correlation_id\":\"9a2d51b0-64bc-41ea-86b1-8f3bcbb65a89\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612182,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"9a2d51b0-64bc-41ea-86b1-8f3bcbb65a89\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [EmailSchedule] STARTING batch process {\"host\":\"docker_lamp_1\"} {\"correlation_id\":\"dceccba8-9e9c-476e-993d-809067884ebe\",\"trace_id\":\"66f8bba8-1712-464b-b25a-418f16736be2\"}\n[2026-05-11 11:06:17] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":612182,\"remote_search\":true,\"lead_id\":null,\"contact_id\":4487,\"account_id\":244,\"opportunity_id\":299,\"stage_id\":36} {\"correlation_id\":\"9a2d51b0-64bc-41ea-86b1-8f3bcbb65a89\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [EmailSchedule] FINISHED batch process {\"host\":\"docker_lamp_1\",\"processed\":0} {\"correlation_id\":\"dceccba8-9e9c-476e-993d-809067884ebe\",\"trace_id\":\"66f8bba8-1712-464b-b25a-418f16736be2\"}\n[2026-05-11 11:06:17] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage for command {\"command\":\"mailbox:batch:process\",\"memoryBeforeCommandInMb\":60.0,\"memoryAfterCommandInMB\":60.0,\"memoryPeakBeforeCommandInMb\":99.727,\"memoryPeakAfterCommandInMB\":99.727} {\"correlation_id\":\"dceccba8-9e9c-476e-993d-809067884ebe\",\"trace_id\":\"66f8bba8-1712-464b-b25a-418f16736be2\"}\n[2026-05-11 11:06:17] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":612181,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":4487,\"account_id\":244,\"opportunity_id\":299,\"stage_id\":36}} {\"correlation_id\":\"8ff40f83-337f-432a-b950-e35779bb59bf\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612181} {\"correlation_id\":\"8ff40f83-337f-432a-b950-e35779bb59bf\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612181,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"8ff40f83-337f-432a-b950-e35779bb59bf\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":612181,\"participants\":[{\"id\":999223,\"user_id\":null,\"contact_id\":4487,\"lead_id\":null},{\"id\":999224,\"user_id\":261,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"8ff40f83-337f-432a-b950-e35779bb59bf\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"8ff40f83-337f-432a-b950-e35779bb59bf\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"8ff40f83-337f-432a-b950-e35779bb59bf\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"8ff40f83-337f-432a-b950-e35779bb59bf\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"8ff40f83-337f-432a-b950-e35779bb59bf\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"robinson@crusoe.com\"} {\"correlation_id\":\"8ff40f83-337f-432a-b950-e35779bb59bf\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":612181,\"team_id\":2,\"email\":\"adelina.petrova@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"8ff40f83-337f-432a-b950-e35779bb59bf\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":612181,\"participants_processed\":2,\"exact_matches\":1,\"domain_matches\":0,\"best_match_found\":true} {\"correlation_id\":\"8ff40f83-337f-432a-b950-e35779bb59bf\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612181} {\"correlation_id\":\"8ff40f83-337f-432a-b950-e35779bb59bf\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612181,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"8ff40f83-337f-432a-b950-e35779bb59bf\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":612181,\"remote_search\":true,\"lead_id\":null,\"contact_id\":4487,\"account_id\":244,\"opportunity_id\":299,\"stage_id\":36} {\"correlation_id\":\"8ff40f83-337f-432a-b950-e35779bb59bf\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":612180,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":4487,\"account_id\":244,\"opportunity_id\":299,\"stage_id\":36}} {\"correlation_id\":\"03f05b91-f803-45c9-b4bf-6a5f8ddf4a2c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612180} {\"correlation_id\":\"03f05b91-f803-45c9-b4bf-6a5f8ddf4a2c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612180,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"03f05b91-f803-45c9-b4bf-6a5f8ddf4a2c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:17] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":612180,\"participants\":[{\"id\":999221,\"user_id\":null,\"contact_id\":4487,\"lead_id\":null},{\"id\":999222,\"user_id\":261,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"03f05b91-f803-45c9-b4bf-6a5f8ddf4a2c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"03f05b91-f803-45c9-b4bf-6a5f8ddf4a2c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"03f05b91-f803-45c9-b4bf-6a5f8ddf4a2c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"03f05b91-f803-45c9-b4bf-6a5f8ddf4a2c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"03f05b91-f803-45c9-b4bf-6a5f8ddf4a2c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"robinson@crusoe.com\"} {\"correlation_id\":\"03f05b91-f803-45c9-b4bf-6a5f8ddf4a2c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":612180,\"team_id\":2,\"email\":\"adelina.petrova@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"03f05b91-f803-45c9-b4bf-6a5f8ddf4a2c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":612180,\"participants_processed\":2,\"exact_matches\":1,\"domain_matches\":0,\"best_match_found\":true} {\"correlation_id\":\"03f05b91-f803-45c9-b4bf-6a5f8ddf4a2c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":612180} {\"correlation_id\":\"03f05b91-f803-45c9-b4bf-6a5f8ddf4a2c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":612180,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"03f05b91-f803-45c9-b4bf-6a5f8ddf4a2c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":612180,\"remote_search\":true,\"lead_id\":null,\"contact_id\":4487,\"account_id\":244,\"opportunity_id\":299,\"stage_id\":36} {\"correlation_id\":\"03f05b91-f803-45c9-b4bf-6a5f8ddf4a2c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":611455,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89}} {\"correlation_id\":\"299c67e4-7026-46ee-b10d-d4e48fb55a7c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":611455} {\"correlation_id\":\"299c67e4-7026-46ee-b10d-d4e48fb55a7c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":611455,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"299c67e4-7026-46ee-b10d-d4e48fb55a7c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":611455,\"participants\":[{\"id\":997961,\"user_id\":null,\"contact_id\":null,\"lead_id\":null},{\"id\":997962,\"user_id\":1460,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"299c67e4-7026-46ee-b10d-d4e48fb55a7c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"299c67e4-7026-46ee-b10d-d4e48fb55a7c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"299c67e4-7026-46ee-b10d-d4e48fb55a7c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"299c67e4-7026-46ee-b10d-d4e48fb55a7c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"299c67e4-7026-46ee-b10d-d4e48fb55a7c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [Prospect match] Cache miss, calling the API {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"299c67e4-7026-46ee-b10d-d4e48fb55a7c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [Hubspot] Failed to fetch contact {\"email\":\"support@staging.jiminny.com\",\"reason\":\"[404] Client error: `GET https://api.hubapi.com/crm/v3/objects/contacts/support%40staging.jiminny.com?properties=email%2Cfirstname%2Clastname%2Ccountry%2Cphone%2Cmobilephone%2Cjobtitle%2Chubspot_owner_id%2Cassociatedcompanyid%2Cphoto&archived=0&idProperty=email` resulted in a `404 Not Found` response\"} {\"correlation_id\":\"299c67e4-7026-46ee-b10d-d4e48fb55a7c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [Prospect match] API returned empty result, caching the miss with empty prospect data {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"299c67e4-7026-46ee-b10d-d4e48fb55a7c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [Hubspot] Pagination completed {\"team_id\":2,\"endpoint\":\"https://api.hubapi.com/crm/v3/objects/contact/search\",\"total_requests\":1,\"total_records_fetched\":0,\"total_elapsed_seconds\":0.24,\"average_seconds_per_request\":0.24} {\"correlation_id\":\"299c67e4-7026-46ee-b10d-d4e48fb55a7c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [Prospect match] Resolved company domain from email {\"email\":\"support@staging.jiminny.com\",\"domain\":\"jiminny.com\"} {\"correlation_id\":\"299c67e4-7026-46ee-b10d-d4e48fb55a7c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [Prospect match] Cache miss {\"identifier_type\":\"domain\",\"identifier\":\"jiminny.com\",\"crm\":\"hubspot\"} {\"correlation_id\":\"299c67e4-7026-46ee-b10d-d4e48fb55a7c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:18] local.INFO: [Prospect match] Cache miss, calling the API {\"identifier_type\":\"domain\",\"identifier\":\"jiminny.com\"} {\"correlation_id\":\"299c67e4-7026-46ee-b10d-d4e48fb55a7c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:19] local.INFO: [HubSpot] importAccount {\"crm_provider_id\":\"749766179\",\"config_id\":2} {\"correlation_id\":\"299c67e4-7026-46ee-b10d-d4e48fb55a7c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:19] local.INFO: [HubSpot] CRM Search requested {\"request\":{\"filterGroups\":[{\"filters\":[{\"propertyName\":\"associations.company\",\"operator\":\"EQ\",\"value\":\"749766179\"},{\"propertyName\":\"dealstage\",\"operator\":\"NOT_IN\",\"values\":[\"closedwon\",\"4040964\",\"59247967\"]},{\"propertyName\":\"dealstage\",\"operator\":\"NOT_IN\",\"values\":[\"closedlost\",\"4040965\",\"59247968\"]}]}],\"sorts\":[{\"propertyName\":\"modifieddate\",\"direction\":\"DESCENDING\"}],\"properties\":[\"dealname\",\"amount\",\"hubspot_owner_id\",\"pipeline\",\"dealstage\",\"closedate\",\"deal_currency_code\"],\"limit\":200}} {\"correlation_id\":\"299c67e4-7026-46ee-b10d-d4e48fb55a7c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:19] local.INFO: [Hubspot] Pagination completed {\"team_id\":2,\"endpoint\":\"https://api.hubapi.com/crm/v3/objects/deals/search\",\"total_requests\":1,\"total_records_fetched\":10,\"total_elapsed_seconds\":0.34,\"average_seconds_per_request\":0.34} {\"correlation_id\":\"299c67e4-7026-46ee-b10d-d4e48fb55a7c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:20] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":611455,\"team_id\":2,\"email\":\"aneliya.angelova@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"299c67e4-7026-46ee-b10d-d4e48fb55a7c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:21] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":611455,\"participants_processed\":2,\"exact_matches\":0,\"domain_matches\":1,\"best_match_found\":true} {\"correlation_id\":\"299c67e4-7026-46ee-b10d-d4e48fb55a7c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:21] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":611455} {\"correlation_id\":\"299c67e4-7026-46ee-b10d-d4e48fb55a7c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:21] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":611455,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"299c67e4-7026-46ee-b10d-d4e48fb55a7c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:21] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":611455,\"remote_search\":true,\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89} {\"correlation_id\":\"299c67e4-7026-46ee-b10d-d4e48fb55a7c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:21] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":611451,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89}} {\"correlation_id\":\"2385adb3-c893-49f8-9fbc-65b354009bea\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:21] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":611451} {\"correlation_id\":\"2385adb3-c893-49f8-9fbc-65b354009bea\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:21] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":611451,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"2385adb3-c893-49f8-9fbc-65b354009bea\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:21] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":611451,\"participants\":[{\"id\":997955,\"user_id\":null,\"contact_id\":null,\"lead_id\":null},{\"id\":997956,\"user_id\":18,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"2385adb3-c893-49f8-9fbc-65b354009bea\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:21] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"2385adb3-c893-49f8-9fbc-65b354009bea\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:21] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"2385adb3-c893-49f8-9fbc-65b354009bea\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:21] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"2385adb3-c893-49f8-9fbc-65b354009bea\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:21] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"2385adb3-c893-49f8-9fbc-65b354009bea\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:21] local.INFO: [Prospect match] Cache miss, calling the API {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"2385adb3-c893-49f8-9fbc-65b354009bea\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:23] local.INFO: [Hubspot] Failed to fetch contact {\"email\":\"support@staging.jiminny.com\",\"reason\":\"[404] Client error: `GET https://api.hubapi.com/crm/v3/objects/contacts/support%40staging.jiminny.com?properties=email%2Cfirstname%2Clastname%2Ccountry%2Cphone%2Cmobilephone%2Cjobtitle%2Chubspot_owner_id%2Cassociatedcompanyid%2Cphoto&archived=0&idProperty=email` resulted in a `404 Not Found` response\"} {\"correlation_id\":\"2385adb3-c893-49f8-9fbc-65b354009bea\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:23] local.INFO: [Prospect match] API returned empty result, caching the miss with empty prospect data {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"2385adb3-c893-49f8-9fbc-65b354009bea\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:23] local.INFO: [Hubspot] Pagination completed {\"team_id\":2,\"endpoint\":\"https://api.hubapi.com/crm/v3/objects/contact/search\",\"total_requests\":1,\"total_records_fetched\":0,\"total_elapsed_seconds\":0.49,\"average_seconds_per_request\":0.49} {\"correlation_id\":\"2385adb3-c893-49f8-9fbc-65b354009bea\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:24] local.INFO: [Prospect match] Resolved company domain from email {\"email\":\"support@staging.jiminny.com\",\"domain\":\"jiminny.com\"} {\"correlation_id\":\"2385adb3-c893-49f8-9fbc-65b354009bea\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:24] local.INFO: [Prospect match] Cache miss {\"identifier_type\":\"domain\",\"identifier\":\"jiminny.com\",\"crm\":\"hubspot\"} {\"correlation_id\":\"2385adb3-c893-49f8-9fbc-65b354009bea\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:24] local.INFO: [Prospect match] Cache miss, calling the API {\"identifier_type\":\"domain\",\"identifier\":\"jiminny.com\"} {\"correlation_id\":\"2385adb3-c893-49f8-9fbc-65b354009bea\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:24] local.INFO: [HubSpot] importAccount {\"crm_provider_id\":\"749766179\",\"config_id\":2} {\"correlation_id\":\"2385adb3-c893-49f8-9fbc-65b354009bea\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:24] local.INFO: [HubSpot] CRM Search requested {\"request\":{\"filterGroups\":[{\"filters\":[{\"propertyName\":\"associations.company\",\"operator\":\"EQ\",\"value\":\"749766179\"},{\"propertyName\":\"dealstage\",\"operator\":\"NOT_IN\",\"values\":[\"closedwon\",\"4040964\",\"59247967\"]},{\"propertyName\":\"dealstage\",\"operator\":\"NOT_IN\",\"values\":[\"closedlost\",\"4040965\",\"59247968\"]}]}],\"sorts\":[{\"propertyName\":\"modifieddate\",\"direction\":\"DESCENDING\"}],\"properties\":[\"dealname\",\"amount\",\"hubspot_owner_id\",\"pipeline\",\"dealstage\",\"closedate\",\"deal_currency_code\"],\"limit\":200}} {\"correlation_id\":\"2385adb3-c893-49f8-9fbc-65b354009bea\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:24] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"activity:purge-stale\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.727} {\"correlation_id\":\"1b5b216e-e0a9-4898-9f26-180cd26a0438\",\"trace_id\":\"f8d4ad10-fc79-469f-a5b8-e89d8fe8c392\"}\n[2026-05-11 11:06:24] local.INFO: [ EsUpdateProcessManager ] Finished updating entities in ES {\"worker\":\"\",\"peak_memory\":\"99.73 MB\",\"elapsed_seconds\":3.18,\"update_target\":\"activities\",\"should_iterate_again\":false} {\"correlation_id\":\"38935ae1-fcb3-437e-9c9e-1b7415a0b7e1\",\"trace_id\":\"325a2f9b-9c66-4223-ac02-1a43723f4315\"}\n[2026-05-11 11:06:24] local.INFO: [Hubspot] Pagination completed {\"team_id\":2,\"endpoint\":\"https://api.hubapi.com/crm/v3/objects/deals/search\",\"total_requests\":1,\"total_records_fetched\":10,\"total_elapsed_seconds\":0.26,\"average_seconds_per_request\":0.26} {\"correlation_id\":\"2385adb3-c893-49f8-9fbc-65b354009bea\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:25] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage for command {\"command\":\"activity:purge-stale\",\"memoryBeforeCommandInMb\":60.0,\"memoryAfterCommandInMB\":60.0,\"memoryPeakBeforeCommandInMb\":99.727,\"memoryPeakAfterCommandInMB\":99.727} {\"correlation_id\":\"1b5b216e-e0a9-4898-9f26-180cd26a0438\",\"trace_id\":\"f8d4ad10-fc79-469f-a5b8-e89d8fe8c392\"}\n[2026-05-11 11:06:25] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":611451,\"team_id\":2,\"email\":\"veselin.kulov@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"2385adb3-c893-49f8-9fbc-65b354009bea\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:25] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":611451,\"participants_processed\":2,\"exact_matches\":0,\"domain_matches\":1,\"best_match_found\":true} {\"correlation_id\":\"2385adb3-c893-49f8-9fbc-65b354009bea\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:25] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":611451} {\"correlation_id\":\"2385adb3-c893-49f8-9fbc-65b354009bea\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:25] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":611451,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"2385adb3-c893-49f8-9fbc-65b354009bea\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:25] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":611451,\"remote_search\":true,\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89} {\"correlation_id\":\"2385adb3-c893-49f8-9fbc-65b354009bea\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":611087,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89}} {\"correlation_id\":\"a6d1c39e-8bb7-43da-81ba-7a22dc8de463\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":611087} {\"correlation_id\":\"a6d1c39e-8bb7-43da-81ba-7a22dc8de463\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":611087,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"a6d1c39e-8bb7-43da-81ba-7a22dc8de463\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":611087,\"participants\":[{\"id\":997368,\"user_id\":null,\"contact_id\":null,\"lead_id\":null},{\"id\":997369,\"user_id\":18,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"a6d1c39e-8bb7-43da-81ba-7a22dc8de463\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"a6d1c39e-8bb7-43da-81ba-7a22dc8de463\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"a6d1c39e-8bb7-43da-81ba-7a22dc8de463\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"a6d1c39e-8bb7-43da-81ba-7a22dc8de463\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"a6d1c39e-8bb7-43da-81ba-7a22dc8de463\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"a6d1c39e-8bb7-43da-81ba-7a22dc8de463\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [Prospect match] cached empty result - no API calls, try next matching method {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"a6d1c39e-8bb7-43da-81ba-7a22dc8de463\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [Prospect match] Resolved company domain from email {\"email\":\"support@staging.jiminny.com\",\"domain\":\"jiminny.com\"} {\"correlation_id\":\"a6d1c39e-8bb7-43da-81ba-7a22dc8de463\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":611087,\"team_id\":2,\"email\":\"veselin.kulov@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"a6d1c39e-8bb7-43da-81ba-7a22dc8de463\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":611087,\"participants_processed\":2,\"exact_matches\":0,\"domain_matches\":1,\"best_match_found\":true} {\"correlation_id\":\"a6d1c39e-8bb7-43da-81ba-7a22dc8de463\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":611087} {\"correlation_id\":\"a6d1c39e-8bb7-43da-81ba-7a22dc8de463\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":611087,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"a6d1c39e-8bb7-43da-81ba-7a22dc8de463\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":611087,\"remote_search\":true,\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89} {\"correlation_id\":\"a6d1c39e-8bb7-43da-81ba-7a22dc8de463\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":611076,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89}} {\"correlation_id\":\"8905b485-a3d2-474a-a2dd-dd5375deb178\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":611076} {\"correlation_id\":\"8905b485-a3d2-474a-a2dd-dd5375deb178\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":611076,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"8905b485-a3d2-474a-a2dd-dd5375deb178\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":611076,\"participants\":[{\"id\":997346,\"user_id\":null,\"contact_id\":null,\"lead_id\":null},{\"id\":997347,\"user_id\":18,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"8905b485-a3d2-474a-a2dd-dd5375deb178\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"8905b485-a3d2-474a-a2dd-dd5375deb178\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"8905b485-a3d2-474a-a2dd-dd5375deb178\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"8905b485-a3d2-474a-a2dd-dd5375deb178\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"8905b485-a3d2-474a-a2dd-dd5375deb178\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"8905b485-a3d2-474a-a2dd-dd5375deb178\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [Prospect match] cached empty result - no API calls, try next matching method {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"8905b485-a3d2-474a-a2dd-dd5375deb178\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [Prospect match] Resolved company domain from email {\"email\":\"support@staging.jiminny.com\",\"domain\":\"jiminny.com\"} {\"correlation_id\":\"8905b485-a3d2-474a-a2dd-dd5375deb178\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":611076,\"team_id\":2,\"email\":\"veselin.kulov@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"8905b485-a3d2-474a-a2dd-dd5375deb178\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":611076,\"participants_processed\":2,\"exact_matches\":0,\"domain_matches\":1,\"best_match_found\":true} {\"correlation_id\":\"8905b485-a3d2-474a-a2dd-dd5375deb178\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":611076} {\"correlation_id\":\"8905b485-a3d2-474a-a2dd-dd5375deb178\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":611076,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"8905b485-a3d2-474a-a2dd-dd5375deb178\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":611076,\"remote_search\":true,\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89} {\"correlation_id\":\"8905b485-a3d2-474a-a2dd-dd5375deb178\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":610935,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89}} {\"correlation_id\":\"77fcbcd0-06e6-4680-85f5-cdefdd76007c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610935} {\"correlation_id\":\"77fcbcd0-06e6-4680-85f5-cdefdd76007c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610935,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"77fcbcd0-06e6-4680-85f5-cdefdd76007c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":610935,\"participants\":[{\"id\":997141,\"user_id\":null,\"contact_id\":null,\"lead_id\":null},{\"id\":997142,\"user_id\":18,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"77fcbcd0-06e6-4680-85f5-cdefdd76007c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"77fcbcd0-06e6-4680-85f5-cdefdd76007c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"77fcbcd0-06e6-4680-85f5-cdefdd76007c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"77fcbcd0-06e6-4680-85f5-cdefdd76007c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"77fcbcd0-06e6-4680-85f5-cdefdd76007c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"77fcbcd0-06e6-4680-85f5-cdefdd76007c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [Prospect match] cached empty result - no API calls, try next matching method {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"77fcbcd0-06e6-4680-85f5-cdefdd76007c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [Prospect match] Resolved company domain from email {\"email\":\"support@staging.jiminny.com\",\"domain\":\"jiminny.com\"} {\"correlation_id\":\"77fcbcd0-06e6-4680-85f5-cdefdd76007c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":610935,\"team_id\":2,\"email\":\"veselin.kulov@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"77fcbcd0-06e6-4680-85f5-cdefdd76007c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":610935,\"participants_processed\":2,\"exact_matches\":0,\"domain_matches\":1,\"best_match_found\":true} {\"correlation_id\":\"77fcbcd0-06e6-4680-85f5-cdefdd76007c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610935} {\"correlation_id\":\"77fcbcd0-06e6-4680-85f5-cdefdd76007c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610935,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"77fcbcd0-06e6-4680-85f5-cdefdd76007c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":610935,\"remote_search\":true,\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89} {\"correlation_id\":\"77fcbcd0-06e6-4680-85f5-cdefdd76007c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":610915,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89}} {\"correlation_id\":\"79639904-266b-4873-8940-014db94b5d8f\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610915} {\"correlation_id\":\"79639904-266b-4873-8940-014db94b5d8f\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610915,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"79639904-266b-4873-8940-014db94b5d8f\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":610915,\"participants\":[{\"id\":997104,\"user_id\":null,\"contact_id\":null,\"lead_id\":null},{\"id\":997105,\"user_id\":18,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"79639904-266b-4873-8940-014db94b5d8f\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"79639904-266b-4873-8940-014db94b5d8f\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"79639904-266b-4873-8940-014db94b5d8f\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"79639904-266b-4873-8940-014db94b5d8f\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"79639904-266b-4873-8940-014db94b5d8f\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"79639904-266b-4873-8940-014db94b5d8f\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [Prospect match] cached empty result - no API calls, try next matching method {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"79639904-266b-4873-8940-014db94b5d8f\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [Prospect match] Resolved company domain from email {\"email\":\"support@staging.jiminny.com\",\"domain\":\"jiminny.com\"} {\"correlation_id\":\"79639904-266b-4873-8940-014db94b5d8f\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":610915,\"team_id\":2,\"email\":\"veselin.kulov@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"79639904-266b-4873-8940-014db94b5d8f\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":610915,\"participants_processed\":2,\"exact_matches\":0,\"domain_matches\":1,\"best_match_found\":true} {\"correlation_id\":\"79639904-266b-4873-8940-014db94b5d8f\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610915} {\"correlation_id\":\"79639904-266b-4873-8940-014db94b5d8f\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610915,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"79639904-266b-4873-8940-014db94b5d8f\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:26] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":610915,\"remote_search\":true,\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89} {\"correlation_id\":\"79639904-266b-4873-8940-014db94b5d8f\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":610900,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89}} {\"correlation_id\":\"068ab494-a35a-4562-b447-44bf2ddb775d\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610900} {\"correlation_id\":\"068ab494-a35a-4562-b447-44bf2ddb775d\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610900,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"068ab494-a35a-4562-b447-44bf2ddb775d\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":610900,\"participants\":[{\"id\":997081,\"user_id\":null,\"contact_id\":null,\"lead_id\":null},{\"id\":997082,\"user_id\":18,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"068ab494-a35a-4562-b447-44bf2ddb775d\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"068ab494-a35a-4562-b447-44bf2ddb775d\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"068ab494-a35a-4562-b447-44bf2ddb775d\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"068ab494-a35a-4562-b447-44bf2ddb775d\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"068ab494-a35a-4562-b447-44bf2ddb775d\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"068ab494-a35a-4562-b447-44bf2ddb775d\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [Prospect match] cached empty result - no API calls, try next matching method {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"068ab494-a35a-4562-b447-44bf2ddb775d\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [Prospect match] Resolved company domain from email {\"email\":\"support@staging.jiminny.com\",\"domain\":\"jiminny.com\"} {\"correlation_id\":\"068ab494-a35a-4562-b447-44bf2ddb775d\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":610900,\"team_id\":2,\"email\":\"veselin.kulov@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"068ab494-a35a-4562-b447-44bf2ddb775d\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":610900,\"participants_processed\":2,\"exact_matches\":0,\"domain_matches\":1,\"best_match_found\":true} {\"correlation_id\":\"068ab494-a35a-4562-b447-44bf2ddb775d\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610900} {\"correlation_id\":\"068ab494-a35a-4562-b447-44bf2ddb775d\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610900,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"068ab494-a35a-4562-b447-44bf2ddb775d\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":610900,\"remote_search\":true,\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89} {\"correlation_id\":\"068ab494-a35a-4562-b447-44bf2ddb775d\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":610885,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89}} {\"correlation_id\":\"a556c8c6-d98c-4c17-b45c-e5ba57a68062\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610885} {\"correlation_id\":\"a556c8c6-d98c-4c17-b45c-e5ba57a68062\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610885,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"a556c8c6-d98c-4c17-b45c-e5ba57a68062\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":610885,\"participants\":[{\"id\":997051,\"user_id\":null,\"contact_id\":null,\"lead_id\":null},{\"id\":997052,\"user_id\":18,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"a556c8c6-d98c-4c17-b45c-e5ba57a68062\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"a556c8c6-d98c-4c17-b45c-e5ba57a68062\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"a556c8c6-d98c-4c17-b45c-e5ba57a68062\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"a556c8c6-d98c-4c17-b45c-e5ba57a68062\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"a556c8c6-d98c-4c17-b45c-e5ba57a68062\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"a556c8c6-d98c-4c17-b45c-e5ba57a68062\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [Prospect match] cached empty result - no API calls, try next matching method {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"a556c8c6-d98c-4c17-b45c-e5ba57a68062\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [Prospect match] Resolved company domain from email {\"email\":\"support@staging.jiminny.com\",\"domain\":\"jiminny.com\"} {\"correlation_id\":\"a556c8c6-d98c-4c17-b45c-e5ba57a68062\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":610885,\"team_id\":2,\"email\":\"veselin.kulov@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"a556c8c6-d98c-4c17-b45c-e5ba57a68062\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":610885,\"participants_processed\":2,\"exact_matches\":0,\"domain_matches\":1,\"best_match_found\":true} {\"correlation_id\":\"a556c8c6-d98c-4c17-b45c-e5ba57a68062\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610885} {\"correlation_id\":\"a556c8c6-d98c-4c17-b45c-e5ba57a68062\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610885,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"a556c8c6-d98c-4c17-b45c-e5ba57a68062\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":610885,\"remote_search\":true,\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89} {\"correlation_id\":\"a556c8c6-d98c-4c17-b45c-e5ba57a68062\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":610878,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89}} {\"correlation_id\":\"915d4deb-56d3-4de7-bc74-2274b327505c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610878} {\"correlation_id\":\"915d4deb-56d3-4de7-bc74-2274b327505c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610878,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"915d4deb-56d3-4de7-bc74-2274b327505c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":610878,\"participants\":[{\"id\":997035,\"user_id\":null,\"contact_id\":null,\"lead_id\":null},{\"id\":997036,\"user_id\":18,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"915d4deb-56d3-4de7-bc74-2274b327505c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"915d4deb-56d3-4de7-bc74-2274b327505c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"915d4deb-56d3-4de7-bc74-2274b327505c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"915d4deb-56d3-4de7-bc74-2274b327505c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"915d4deb-56d3-4de7-bc74-2274b327505c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"915d4deb-56d3-4de7-bc74-2274b327505c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [Prospect match] cached empty result - no API calls, try next matching method {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"915d4deb-56d3-4de7-bc74-2274b327505c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [Prospect match] Resolved company domain from email {\"email\":\"support@staging.jiminny.com\",\"domain\":\"jiminny.com\"} {\"correlation_id\":\"915d4deb-56d3-4de7-bc74-2274b327505c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":610878,\"team_id\":2,\"email\":\"veselin.kulov@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"915d4deb-56d3-4de7-bc74-2274b327505c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":610878,\"participants_processed\":2,\"exact_matches\":0,\"domain_matches\":1,\"best_match_found\":true} {\"correlation_id\":\"915d4deb-56d3-4de7-bc74-2274b327505c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610878} {\"correlation_id\":\"915d4deb-56d3-4de7-bc74-2274b327505c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610878,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"915d4deb-56d3-4de7-bc74-2274b327505c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":610878,\"remote_search\":true,\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89} {\"correlation_id\":\"915d4deb-56d3-4de7-bc74-2274b327505c\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":610874,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89}} {\"correlation_id\":\"1b89c98c-65d3-4286-b140-c9453bca9a74\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610874} {\"correlation_id\":\"1b89c98c-65d3-4286-b140-c9453bca9a74\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610874,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"1b89c98c-65d3-4286-b140-c9453bca9a74\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":610874,\"participants\":[{\"id\":997025,\"user_id\":null,\"contact_id\":null,\"lead_id\":null},{\"id\":997026,\"user_id\":18,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"1b89c98c-65d3-4286-b140-c9453bca9a74\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"1b89c98c-65d3-4286-b140-c9453bca9a74\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"1b89c98c-65d3-4286-b140-c9453bca9a74\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"1b89c98c-65d3-4286-b140-c9453bca9a74\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"1b89c98c-65d3-4286-b140-c9453bca9a74\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"1b89c98c-65d3-4286-b140-c9453bca9a74\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [Prospect match] cached empty result - no API calls, try next matching method {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"1b89c98c-65d3-4286-b140-c9453bca9a74\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [Prospect match] Resolved company domain from email {\"email\":\"support@staging.jiminny.com\",\"domain\":\"jiminny.com\"} {\"correlation_id\":\"1b89c98c-65d3-4286-b140-c9453bca9a74\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":610874,\"team_id\":2,\"email\":\"veselin.kulov@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"1b89c98c-65d3-4286-b140-c9453bca9a74\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":610874,\"participants_processed\":2,\"exact_matches\":0,\"domain_matches\":1,\"best_match_found\":true} {\"correlation_id\":\"1b89c98c-65d3-4286-b140-c9453bca9a74\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610874} {\"correlation_id\":\"1b89c98c-65d3-4286-b140-c9453bca9a74\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610874,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"1b89c98c-65d3-4286-b140-c9453bca9a74\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":610874,\"remote_search\":true,\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89} {\"correlation_id\":\"1b89c98c-65d3-4286-b140-c9453bca9a74\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":610867,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89}} {\"correlation_id\":\"8a33657f-373a-4b5d-b1f9-71bbe85bd3ba\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610867} {\"correlation_id\":\"8a33657f-373a-4b5d-b1f9-71bbe85bd3ba\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610867,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"8a33657f-373a-4b5d-b1f9-71bbe85bd3ba\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":610867,\"participants\":[{\"id\":997011,\"user_id\":null,\"contact_id\":null,\"lead_id\":null},{\"id\":997012,\"user_id\":18,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"8a33657f-373a-4b5d-b1f9-71bbe85bd3ba\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"8a33657f-373a-4b5d-b1f9-71bbe85bd3ba\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"8a33657f-373a-4b5d-b1f9-71bbe85bd3ba\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"8a33657f-373a-4b5d-b1f9-71bbe85bd3ba\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"8a33657f-373a-4b5d-b1f9-71bbe85bd3ba\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"8a33657f-373a-4b5d-b1f9-71bbe85bd3ba\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [Prospect match] cached empty result - no API calls, try next matching method {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"8a33657f-373a-4b5d-b1f9-71bbe85bd3ba\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [Prospect match] Resolved company domain from email {\"email\":\"support@staging.jiminny.com\",\"domain\":\"jiminny.com\"} {\"correlation_id\":\"8a33657f-373a-4b5d-b1f9-71bbe85bd3ba\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":610867,\"team_id\":2,\"email\":\"veselin.kulov@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"8a33657f-373a-4b5d-b1f9-71bbe85bd3ba\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":610867,\"participants_processed\":2,\"exact_matches\":0,\"domain_matches\":1,\"best_match_found\":true} {\"correlation_id\":\"8a33657f-373a-4b5d-b1f9-71bbe85bd3ba\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610867} {\"correlation_id\":\"8a33657f-373a-4b5d-b1f9-71bbe85bd3ba\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610867,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"8a33657f-373a-4b5d-b1f9-71bbe85bd3ba\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":610867,\"remote_search\":true,\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89} {\"correlation_id\":\"8a33657f-373a-4b5d-b1f9-71bbe85bd3ba\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":610764,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89}} {\"correlation_id\":\"440ce0fb-99c1-458c-9db4-c11e504dd620\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610764} {\"correlation_id\":\"440ce0fb-99c1-458c-9db4-c11e504dd620\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610764,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"440ce0fb-99c1-458c-9db4-c11e504dd620\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":610764,\"participants\":[{\"id\":996951,\"user_id\":null,\"contact_id\":null,\"lead_id\":null},{\"id\":996952,\"user_id\":18,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"440ce0fb-99c1-458c-9db4-c11e504dd620\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"440ce0fb-99c1-458c-9db4-c11e504dd620\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"440ce0fb-99c1-458c-9db4-c11e504dd620\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"440ce0fb-99c1-458c-9db4-c11e504dd620\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"440ce0fb-99c1-458c-9db4-c11e504dd620\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"440ce0fb-99c1-458c-9db4-c11e504dd620\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [Prospect match] cached empty result - no API calls, try next matching method {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"440ce0fb-99c1-458c-9db4-c11e504dd620\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [Prospect match] Resolved company domain from email {\"email\":\"support@staging.jiminny.com\",\"domain\":\"jiminny.com\"} {\"correlation_id\":\"440ce0fb-99c1-458c-9db4-c11e504dd620\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":610764,\"team_id\":2,\"email\":\"veselin.kulov@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"440ce0fb-99c1-458c-9db4-c11e504dd620\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":610764,\"participants_processed\":2,\"exact_matches\":0,\"domain_matches\":1,\"best_match_found\":true} {\"correlation_id\":\"440ce0fb-99c1-458c-9db4-c11e504dd620\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610764} {\"correlation_id\":\"440ce0fb-99c1-458c-9db4-c11e504dd620\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610764,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"440ce0fb-99c1-458c-9db4-c11e504dd620\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:27] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":610764,\"remote_search\":true,\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89} {\"correlation_id\":\"440ce0fb-99c1-458c-9db4-c11e504dd620\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":610617,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89}} {\"correlation_id\":\"d8853964-8fe9-4553-96da-d088b6dfc843\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610617} {\"correlation_id\":\"d8853964-8fe9-4553-96da-d088b6dfc843\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610617,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"d8853964-8fe9-4553-96da-d088b6dfc843\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":610617,\"participants\":[{\"id\":996641,\"user_id\":null,\"contact_id\":null,\"lead_id\":null},{\"id\":996642,\"user_id\":18,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"d8853964-8fe9-4553-96da-d088b6dfc843\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"d8853964-8fe9-4553-96da-d088b6dfc843\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"d8853964-8fe9-4553-96da-d088b6dfc843\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"d8853964-8fe9-4553-96da-d088b6dfc843\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"d8853964-8fe9-4553-96da-d088b6dfc843\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"d8853964-8fe9-4553-96da-d088b6dfc843\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [Prospect match] cached empty result - no API calls, try next matching method {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"d8853964-8fe9-4553-96da-d088b6dfc843\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [Prospect match] Resolved company domain from email {\"email\":\"support@staging.jiminny.com\",\"domain\":\"jiminny.com\"} {\"correlation_id\":\"d8853964-8fe9-4553-96da-d088b6dfc843\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":610617,\"team_id\":2,\"email\":\"veselin.kulov@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"d8853964-8fe9-4553-96da-d088b6dfc843\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":610617,\"participants_processed\":2,\"exact_matches\":0,\"domain_matches\":1,\"best_match_found\":true} {\"correlation_id\":\"d8853964-8fe9-4553-96da-d088b6dfc843\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610617} {\"correlation_id\":\"d8853964-8fe9-4553-96da-d088b6dfc843\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610617,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"d8853964-8fe9-4553-96da-d088b6dfc843\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":610617,\"remote_search\":true,\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89} {\"correlation_id\":\"d8853964-8fe9-4553-96da-d088b6dfc843\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":610539,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89}} {\"correlation_id\":\"d021767c-298a-4ad0-9f72-dca085cc6528\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610539} {\"correlation_id\":\"d021767c-298a-4ad0-9f72-dca085cc6528\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610539,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"d021767c-298a-4ad0-9f72-dca085cc6528\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":610539,\"participants\":[{\"id\":996485,\"user_id\":null,\"contact_id\":null,\"lead_id\":null},{\"id\":996486,\"user_id\":18,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"d021767c-298a-4ad0-9f72-dca085cc6528\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"d021767c-298a-4ad0-9f72-dca085cc6528\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"d021767c-298a-4ad0-9f72-dca085cc6528\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"d021767c-298a-4ad0-9f72-dca085cc6528\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"d021767c-298a-4ad0-9f72-dca085cc6528\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"d021767c-298a-4ad0-9f72-dca085cc6528\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [Prospect match] cached empty result - no API calls, try next matching method {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"d021767c-298a-4ad0-9f72-dca085cc6528\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [Prospect match] Resolved company domain from email {\"email\":\"support@staging.jiminny.com\",\"domain\":\"jiminny.com\"} {\"correlation_id\":\"d021767c-298a-4ad0-9f72-dca085cc6528\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":610539,\"team_id\":2,\"email\":\"veselin.kulov@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"d021767c-298a-4ad0-9f72-dca085cc6528\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":610539,\"participants_processed\":2,\"exact_matches\":0,\"domain_matches\":1,\"best_match_found\":true} {\"correlation_id\":\"d021767c-298a-4ad0-9f72-dca085cc6528\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610539} {\"correlation_id\":\"d021767c-298a-4ad0-9f72-dca085cc6528\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610539,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"d021767c-298a-4ad0-9f72-dca085cc6528\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"mailbox:text-relay:sync\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.727} {\"correlation_id\":\"7c0c4db8-2e4e-4aa1-aa71-a3d63cdb81d7\",\"trace_id\":\"b3e9e2f5-3256-429a-ad76-e46e3c2e626d\"}\n[2026-05-11 11:06:28] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":610539,\"remote_search\":true,\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89} {\"correlation_id\":\"d021767c-298a-4ad0-9f72-dca085cc6528\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":610528,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89}} {\"correlation_id\":\"2410a4b8-a997-4cf1-99f0-c23ba54805c7\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610528} {\"correlation_id\":\"2410a4b8-a997-4cf1-99f0-c23ba54805c7\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610528,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"2410a4b8-a997-4cf1-99f0-c23ba54805c7\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":610528,\"participants\":[{\"id\":996463,\"user_id\":null,\"contact_id\":null,\"lead_id\":null},{\"id\":996464,\"user_id\":18,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"2410a4b8-a997-4cf1-99f0-c23ba54805c7\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"2410a4b8-a997-4cf1-99f0-c23ba54805c7\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"2410a4b8-a997-4cf1-99f0-c23ba54805c7\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"2410a4b8-a997-4cf1-99f0-c23ba54805c7\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"2410a4b8-a997-4cf1-99f0-c23ba54805c7\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"2410a4b8-a997-4cf1-99f0-c23ba54805c7\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [Prospect match] cached empty result - no API calls, try next matching method {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"2410a4b8-a997-4cf1-99f0-c23ba54805c7\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [Prospect match] Resolved company domain from email {\"email\":\"support@staging.jiminny.com\",\"domain\":\"jiminny.com\"} {\"correlation_id\":\"2410a4b8-a997-4cf1-99f0-c23ba54805c7\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":610528,\"team_id\":2,\"email\":\"veselin.kulov@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"2410a4b8-a997-4cf1-99f0-c23ba54805c7\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":610528,\"participants_processed\":2,\"exact_matches\":0,\"domain_matches\":1,\"best_match_found\":true} {\"correlation_id\":\"2410a4b8-a997-4cf1-99f0-c23ba54805c7\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610528} {\"correlation_id\":\"2410a4b8-a997-4cf1-99f0-c23ba54805c7\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610528,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"2410a4b8-a997-4cf1-99f0-c23ba54805c7\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":610528,\"remote_search\":true,\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89} {\"correlation_id\":\"2410a4b8-a997-4cf1-99f0-c23ba54805c7\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":610506,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89}} {\"correlation_id\":\"8b5bce8c-7e89-46ba-b04b-9b9ab4e620b6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610506} {\"correlation_id\":\"8b5bce8c-7e89-46ba-b04b-9b9ab4e620b6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610506,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"8b5bce8c-7e89-46ba-b04b-9b9ab4e620b6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":610506,\"participants\":[{\"id\":996419,\"user_id\":null,\"contact_id\":null,\"lead_id\":null},{\"id\":996420,\"user_id\":18,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"8b5bce8c-7e89-46ba-b04b-9b9ab4e620b6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"8b5bce8c-7e89-46ba-b04b-9b9ab4e620b6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"8b5bce8c-7e89-46ba-b04b-9b9ab4e620b6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"8b5bce8c-7e89-46ba-b04b-9b9ab4e620b6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"8b5bce8c-7e89-46ba-b04b-9b9ab4e620b6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"8b5bce8c-7e89-46ba-b04b-9b9ab4e620b6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [Prospect match] cached empty result - no API calls, try next matching method {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"8b5bce8c-7e89-46ba-b04b-9b9ab4e620b6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [Prospect match] Resolved company domain from email {\"email\":\"support@staging.jiminny.com\",\"domain\":\"jiminny.com\"} {\"correlation_id\":\"8b5bce8c-7e89-46ba-b04b-9b9ab4e620b6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":610506,\"team_id\":2,\"email\":\"veselin.kulov@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"8b5bce8c-7e89-46ba-b04b-9b9ab4e620b6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":610506,\"participants_processed\":2,\"exact_matches\":0,\"domain_matches\":1,\"best_match_found\":true} {\"correlation_id\":\"8b5bce8c-7e89-46ba-b04b-9b9ab4e620b6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610506} {\"correlation_id\":\"8b5bce8c-7e89-46ba-b04b-9b9ab4e620b6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610506,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"8b5bce8c-7e89-46ba-b04b-9b9ab4e620b6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":610506,\"remote_search\":true,\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89} {\"correlation_id\":\"8b5bce8c-7e89-46ba-b04b-9b9ab4e620b6\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":610497,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89}} {\"correlation_id\":\"a9e55e87-8951-406b-84a8-6fecf2841584\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610497} {\"correlation_id\":\"a9e55e87-8951-406b-84a8-6fecf2841584\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610497,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"a9e55e87-8951-406b-84a8-6fecf2841584\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":610497,\"participants\":[{\"id\":996401,\"user_id\":null,\"contact_id\":null,\"lead_id\":null},{\"id\":996402,\"user_id\":18,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"a9e55e87-8951-406b-84a8-6fecf2841584\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"a9e55e87-8951-406b-84a8-6fecf2841584\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"a9e55e87-8951-406b-84a8-6fecf2841584\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"a9e55e87-8951-406b-84a8-6fecf2841584\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"a9e55e87-8951-406b-84a8-6fecf2841584\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"a9e55e87-8951-406b-84a8-6fecf2841584\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [Prospect match] cached empty result - no API calls, try next matching method {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"a9e55e87-8951-406b-84a8-6fecf2841584\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [Prospect match] Resolved company domain from email {\"email\":\"support@staging.jiminny.com\",\"domain\":\"jiminny.com\"} {\"correlation_id\":\"a9e55e87-8951-406b-84a8-6fecf2841584\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":610497,\"team_id\":2,\"email\":\"veselin.kulov@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"a9e55e87-8951-406b-84a8-6fecf2841584\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":610497,\"participants_processed\":2,\"exact_matches\":0,\"domain_matches\":1,\"best_match_found\":true} {\"correlation_id\":\"a9e55e87-8951-406b-84a8-6fecf2841584\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610497} {\"correlation_id\":\"a9e55e87-8951-406b-84a8-6fecf2841584\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610497,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"a9e55e87-8951-406b-84a8-6fecf2841584\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":610497,\"remote_search\":true,\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89} {\"correlation_id\":\"a9e55e87-8951-406b-84a8-6fecf2841584\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":610490,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89}} {\"correlation_id\":\"209ccee3-3f37-4fb8-8648-ff2f554f6cf3\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610490} {\"correlation_id\":\"209ccee3-3f37-4fb8-8648-ff2f554f6cf3\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610490,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"209ccee3-3f37-4fb8-8648-ff2f554f6cf3\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":610490,\"participants\":[{\"id\":996385,\"user_id\":null,\"contact_id\":null,\"lead_id\":null},{\"id\":996386,\"user_id\":18,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"209ccee3-3f37-4fb8-8648-ff2f554f6cf3\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"209ccee3-3f37-4fb8-8648-ff2f554f6cf3\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"209ccee3-3f37-4fb8-8648-ff2f554f6cf3\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"209ccee3-3f37-4fb8-8648-ff2f554f6cf3\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"209ccee3-3f37-4fb8-8648-ff2f554f6cf3\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"209ccee3-3f37-4fb8-8648-ff2f554f6cf3\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [Prospect match] cached empty result - no API calls, try next matching method {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"209ccee3-3f37-4fb8-8648-ff2f554f6cf3\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [Prospect match] Resolved company domain from email {\"email\":\"support@staging.jiminny.com\",\"domain\":\"jiminny.com\"} {\"correlation_id\":\"209ccee3-3f37-4fb8-8648-ff2f554f6cf3\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":610490,\"team_id\":2,\"email\":\"veselin.kulov@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"209ccee3-3f37-4fb8-8648-ff2f554f6cf3\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":610490,\"participants_processed\":2,\"exact_matches\":0,\"domain_matches\":1,\"best_match_found\":true} {\"correlation_id\":\"209ccee3-3f37-4fb8-8648-ff2f554f6cf3\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610490} {\"correlation_id\":\"209ccee3-3f37-4fb8-8648-ff2f554f6cf3\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610490,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"209ccee3-3f37-4fb8-8648-ff2f554f6cf3\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":610490,\"remote_search\":true,\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89} {\"correlation_id\":\"209ccee3-3f37-4fb8-8648-ff2f554f6cf3\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":610470,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89}} {\"correlation_id\":\"0b6c4d04-c34f-4cfd-b391-aa0128a17792\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610470} {\"correlation_id\":\"0b6c4d04-c34f-4cfd-b391-aa0128a17792\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610470,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"0b6c4d04-c34f-4cfd-b391-aa0128a17792\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":610470,\"participants\":[{\"id\":996369,\"user_id\":null,\"contact_id\":null,\"lead_id\":null},{\"id\":996370,\"user_id\":18,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"0b6c4d04-c34f-4cfd-b391-aa0128a17792\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage for command {\"command\":\"mailbox:text-relay:sync\",\"memoryBeforeCommandInMb\":60.0,\"memoryAfterCommandInMB\":60.0,\"memoryPeakBeforeCommandInMb\":99.727,\"memoryPeakAfterCommandInMB\":99.727} {\"correlation_id\":\"7c0c4db8-2e4e-4aa1-aa71-a3d63cdb81d7\",\"trace_id\":\"b3e9e2f5-3256-429a-ad76-e46e3c2e626d\"}\n[2026-05-11 11:06:28] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"0b6c4d04-c34f-4cfd-b391-aa0128a17792\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"0b6c4d04-c34f-4cfd-b391-aa0128a17792\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"0b6c4d04-c34f-4cfd-b391-aa0128a17792\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"0b6c4d04-c34f-4cfd-b391-aa0128a17792\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"0b6c4d04-c34f-4cfd-b391-aa0128a17792\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [Prospect match] cached empty result - no API calls, try next matching method {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"0b6c4d04-c34f-4cfd-b391-aa0128a17792\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [Prospect match] Resolved company domain from email {\"email\":\"support@staging.jiminny.com\",\"domain\":\"jiminny.com\"} {\"correlation_id\":\"0b6c4d04-c34f-4cfd-b391-aa0128a17792\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":610470,\"team_id\":2,\"email\":\"veselin.kulov@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"0b6c4d04-c34f-4cfd-b391-aa0128a17792\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":610470,\"participants_processed\":2,\"exact_matches\":0,\"domain_matches\":1,\"best_match_found\":true} {\"correlation_id\":\"0b6c4d04-c34f-4cfd-b391-aa0128a17792\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610470} {\"correlation_id\":\"0b6c4d04-c34f-4cfd-b391-aa0128a17792\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610470,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"0b6c4d04-c34f-4cfd-b391-aa0128a17792\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":610470,\"remote_search\":true,\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89} {\"correlation_id\":\"0b6c4d04-c34f-4cfd-b391-aa0128a17792\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":610462,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89}} {\"correlation_id\":\"0584d557-927b-47a5-b193-04786c5c20bc\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610462} {\"correlation_id\":\"0584d557-927b-47a5-b193-04786c5c20bc\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610462,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"0584d557-927b-47a5-b193-04786c5c20bc\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":610462,\"participants\":[{\"id\":996353,\"user_id\":null,\"contact_id\":null,\"lead_id\":null},{\"id\":996354,\"user_id\":18,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"0584d557-927b-47a5-b193-04786c5c20bc\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"0584d557-927b-47a5-b193-04786c5c20bc\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"0584d557-927b-47a5-b193-04786c5c20bc\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"0584d557-927b-47a5-b193-04786c5c20bc\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"0584d557-927b-47a5-b193-04786c5c20bc\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"0584d557-927b-47a5-b193-04786c5c20bc\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [Prospect match] cached empty result - no API calls, try next matching method {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"0584d557-927b-47a5-b193-04786c5c20bc\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [Prospect match] Resolved company domain from email {\"email\":\"support@staging.jiminny.com\",\"domain\":\"jiminny.com\"} {\"correlation_id\":\"0584d557-927b-47a5-b193-04786c5c20bc\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":610462,\"team_id\":2,\"email\":\"veselin.kulov@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"0584d557-927b-47a5-b193-04786c5c20bc\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":610462,\"participants_processed\":2,\"exact_matches\":0,\"domain_matches\":1,\"best_match_found\":true} {\"correlation_id\":\"0584d557-927b-47a5-b193-04786c5c20bc\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610462} {\"correlation_id\":\"0584d557-927b-47a5-b193-04786c5c20bc\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610462,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"0584d557-927b-47a5-b193-04786c5c20bc\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":610462,\"remote_search\":true,\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89} {\"correlation_id\":\"0584d557-927b-47a5-b193-04786c5c20bc\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":610451,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89}} {\"correlation_id\":\"e86be547-45b5-48da-b389-b64c7928f856\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610451} {\"correlation_id\":\"e86be547-45b5-48da-b389-b64c7928f856\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610451,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"e86be547-45b5-48da-b389-b64c7928f856\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":610451,\"participants\":[{\"id\":996340,\"user_id\":null,\"contact_id\":null,\"lead_id\":null},{\"id\":996341,\"user_id\":18,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"e86be547-45b5-48da-b389-b64c7928f856\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"e86be547-45b5-48da-b389-b64c7928f856\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"e86be547-45b5-48da-b389-b64c7928f856\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"e86be547-45b5-48da-b389-b64c7928f856\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"e86be547-45b5-48da-b389-b64c7928f856\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"e86be547-45b5-48da-b389-b64c7928f856\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [Prospect match] cached empty result - no API calls, try next matching method {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"e86be547-45b5-48da-b389-b64c7928f856\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [Prospect match] Resolved company domain from email {\"email\":\"support@staging.jiminny.com\",\"domain\":\"jiminny.com\"} {\"correlation_id\":\"e86be547-45b5-48da-b389-b64c7928f856\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":610451,\"team_id\":2,\"email\":\"veselin.kulov@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"e86be547-45b5-48da-b389-b64c7928f856\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":610451,\"participants_processed\":2,\"exact_matches\":0,\"domain_matches\":1,\"best_match_found\":true} {\"correlation_id\":\"e86be547-45b5-48da-b389-b64c7928f856\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610451} {\"correlation_id\":\"e86be547-45b5-48da-b389-b64c7928f856\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610451,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"e86be547-45b5-48da-b389-b64c7928f856\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":610451,\"remote_search\":true,\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89} {\"correlation_id\":\"e86be547-45b5-48da-b389-b64c7928f856\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":610438,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89}} {\"correlation_id\":\"34e69f21-acca-487a-8565-fc42df81c0fe\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610438} {\"correlation_id\":\"34e69f21-acca-487a-8565-fc42df81c0fe\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610438,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"34e69f21-acca-487a-8565-fc42df81c0fe\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":610438,\"participants\":[{\"id\":996320,\"user_id\":null,\"contact_id\":null,\"lead_id\":null},{\"id\":996321,\"user_id\":18,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"34e69f21-acca-487a-8565-fc42df81c0fe\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"34e69f21-acca-487a-8565-fc42df81c0fe\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"34e69f21-acca-487a-8565-fc42df81c0fe\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"34e69f21-acca-487a-8565-fc42df81c0fe\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"34e69f21-acca-487a-8565-fc42df81c0fe\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"34e69f21-acca-487a-8565-fc42df81c0fe\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [Prospect match] cached empty result - no API calls, try next matching method {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"34e69f21-acca-487a-8565-fc42df81c0fe\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [Prospect match] Resolved company domain from email {\"email\":\"support@staging.jiminny.com\",\"domain\":\"jiminny.com\"} {\"correlation_id\":\"34e69f21-acca-487a-8565-fc42df81c0fe\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":610438,\"team_id\":2,\"email\":\"veselin.kulov@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"34e69f21-acca-487a-8565-fc42df81c0fe\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":610438,\"participants_processed\":2,\"exact_matches\":0,\"domain_matches\":1,\"best_match_found\":true} {\"correlation_id\":\"34e69f21-acca-487a-8565-fc42df81c0fe\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610438} {\"correlation_id\":\"34e69f21-acca-487a-8565-fc42df81c0fe\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610438,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"34e69f21-acca-487a-8565-fc42df81c0fe\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:28] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":610438,\"remote_search\":true,\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89} {\"correlation_id\":\"34e69f21-acca-487a-8565-fc42df81c0fe\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":610426,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89}} {\"correlation_id\":\"813c1b3a-95b1-4314-b468-5f42e908f99e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610426} {\"correlation_id\":\"813c1b3a-95b1-4314-b468-5f42e908f99e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610426,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"813c1b3a-95b1-4314-b468-5f42e908f99e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":610426,\"participants\":[{\"id\":996306,\"user_id\":null,\"contact_id\":null,\"lead_id\":null},{\"id\":996307,\"user_id\":18,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"813c1b3a-95b1-4314-b468-5f42e908f99e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"813c1b3a-95b1-4314-b468-5f42e908f99e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"813c1b3a-95b1-4314-b468-5f42e908f99e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"813c1b3a-95b1-4314-b468-5f42e908f99e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"813c1b3a-95b1-4314-b468-5f42e908f99e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"813c1b3a-95b1-4314-b468-5f42e908f99e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [Prospect match] cached empty result - no API calls, try next matching method {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"813c1b3a-95b1-4314-b468-5f42e908f99e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [Prospect match] Resolved company domain from email {\"email\":\"support@staging.jiminny.com\",\"domain\":\"jiminny.com\"} {\"correlation_id\":\"813c1b3a-95b1-4314-b468-5f42e908f99e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":610426,\"team_id\":2,\"email\":\"veselin.kulov@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"813c1b3a-95b1-4314-b468-5f42e908f99e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":610426,\"participants_processed\":2,\"exact_matches\":0,\"domain_matches\":1,\"best_match_found\":true} {\"correlation_id\":\"813c1b3a-95b1-4314-b468-5f42e908f99e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610426} {\"correlation_id\":\"813c1b3a-95b1-4314-b468-5f42e908f99e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610426,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"813c1b3a-95b1-4314-b468-5f42e908f99e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":610426,\"remote_search\":true,\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89} {\"correlation_id\":\"813c1b3a-95b1-4314-b468-5f42e908f99e\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":610403,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89}} {\"correlation_id\":\"98026431-b755-4e7e-af17-86d8e2993031\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610403} {\"correlation_id\":\"98026431-b755-4e7e-af17-86d8e2993031\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610403,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"98026431-b755-4e7e-af17-86d8e2993031\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":610403,\"participants\":[{\"id\":996282,\"user_id\":null,\"contact_id\":null,\"lead_id\":null},{\"id\":996283,\"user_id\":18,\"contact_id\":null,\"lead_id\":null}]} {\"correlation_id\":\"98026431-b755-4e7e-af17-86d8e2993031\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"98026431-b755-4e7e-af17-86d8e2993031\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"98026431-b755-4e7e-af17-86d8e2993031\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"98026431-b755-4e7e-af17-86d8e2993031\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"98026431-b755-4e7e-af17-86d8e2993031\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"98026431-b755-4e7e-af17-86d8e2993031\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [Prospect match] cached empty result - no API calls, try next matching method {\"identifier_type\":\"email\",\"identifier\":\"support@staging.jiminny.com\"} {\"correlation_id\":\"98026431-b755-4e7e-af17-86d8e2993031\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [Prospect match] Resolved company domain from email {\"email\":\"support@staging.jiminny.com\",\"domain\":\"jiminny.com\"} {\"correlation_id\":\"98026431-b755-4e7e-af17-86d8e2993031\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":610403,\"team_id\":2,\"email\":\"veselin.kulov@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"98026431-b755-4e7e-af17-86d8e2993031\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":610403,\"participants_processed\":2,\"exact_matches\":0,\"domain_matches\":1,\"best_match_found\":true} {\"correlation_id\":\"98026431-b755-4e7e-af17-86d8e2993031\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610403} {\"correlation_id\":\"98026431-b755-4e7e-af17-86d8e2993031\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610403,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"98026431-b755-4e7e-af17-86d8e2993031\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":610403,\"remote_search\":true,\"lead_id\":null,\"contact_id\":null,\"account_id\":26,\"opportunity_id\":22,\"stage_id\":89} {\"correlation_id\":\"98026431-b755-4e7e-af17-86d8e2993031\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [MatchActivityCrmData] Starting CRM data matching {\"activity\":610400,\"remote_search\":true,\"set_configuration\":2,\"old_state\":{\"lead_id\":null,\"contact_id\":4487,\"account_id\":244,\"opportunity_id\":350,\"stage_id\":34}} {\"correlation_id\":\"4c991921-b335-438b-a656-aac035483aa5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610400} {\"correlation_id\":\"4c991921-b335-438b-a656-aac035483aa5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610400,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"4c991921-b335-438b-a656-aac035483aa5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [MatchActivityCrmData] Participants old state {\"activity\":610400,\"participants\":[{\"id\":996275,\"user_id\":1460,\"contact_id\":null,\"lead_id\":null},{\"id\":996276,\"user_id\":206,\"contact_id\":null,\"lead_id\":null},{\"id\":996277,\"user_id\":null,\"contact_id\":4487,\"lead_id\":null}]} {\"correlation_id\":\"4c991921-b335-438b-a656-aac035483aa5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [SocialAccountService] Fetching token {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"4c991921-b335-438b-a656-aac035483aa5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [SocialAccountService] Token retrieved {\"socialAccountId\":1499,\"provider\":\"hubspot\"} {\"correlation_id\":\"4c991921-b335-438b-a656-aac035483aa5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"4c991921-b335-438b-a656-aac035483aa5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {\"crm_provider\":\"hubspot\",\"crm_owner\":148,\"team_id\":2} {\"correlation_id\":\"4c991921-b335-438b-a656-aac035483aa5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":610400,\"team_id\":2,\"email\":\"aneliya.angelova@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"4c991921-b335-438b-a656-aac035483aa5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {\"activity_id\":610400,\"team_id\":2,\"email\":\"horen.kirazyan@jiminny.onmicrosoft.com\"} {\"correlation_id\":\"4c991921-b335-438b-a656-aac035483aa5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: ProspectCache - Searching DB for opportunity by owner {\"account_id\":244,\"contact_id\":4487,\"owner_id\":1460} {\"correlation_id\":\"4c991921-b335-438b-a656-aac035483aa5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: ProspectCache - Fallback DB opportunity search {\"account_id\":244,\"contact_id\":4487} {\"correlation_id\":\"4c991921-b335-438b-a656-aac035483aa5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: ProspectCache - Opportunity DB search results {\"account_id\":244,\"contact_id\":4487,\"opportunity_id\":350} {\"correlation_id\":\"4c991921-b335-438b-a656-aac035483aa5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [Prospect match] Cache / local search hit {\"identifier_type\":\"email\",\"identifier\":\"robinson@crusoe.com\"} {\"correlation_id\":\"4c991921-b335-438b-a656-aac035483aa5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [CrmActivityService] CRM matching completed {\"activity_id\":610400,\"participants_processed\":3,\"exact_matches\":1,\"domain_matches\":0,\"best_match_found\":true} {\"correlation_id\":\"4c991921-b335-438b-a656-aac035483aa5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [ EsUpdateTarget ] Update single target {\"target\":\"activities\",\"purpose\":\"searchable-observer-update\",\"entityId\":610400} {\"correlation_id\":\"4c991921-b335-438b-a656-aac035483aa5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {\"entityType\":\"activities\",\"entityId\":610400,\"collectionKey\":\"activities-for-update-priority\",\"withPriority\":true} {\"correlation_id\":\"4c991921-b335-438b-a656-aac035483aa5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {\"activity\":610400,\"remote_search\":true,\"lead_id\":null,\"contact_id\":4487,\"account_id\":244,\"opportunity_id\":350,\"stage_id\":34} {\"correlation_id\":\"4c991921-b335-438b-a656-aac035483aa5\",\"trace_id\":\"064c6959-5450-4b4b-b66f-93c5c058f5d4\"}\n[2026-05-11 11:06:29] local.INFO: [ EsUpdateProcessManager ] Finished updating entities in ES {\"worker\":\"\",\"peak_memory\":\"99.73 MB\",\"elapsed_seconds\":0.14,\"update_target\":\"activities\",\"should_iterate_again\":false} {\"correlation_id\":\"38935ae1-fcb3-437e-9c9e-1b7415a0b7e1\",\"trace_id\":\"325a2f9b-9c66-4223-ac02-1a43723f4315\"}\n[2026-05-11 11:06:30] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"conference:pre-meeting-notification\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.727} {\"correlation_id\":\"4fa18424-38b1-4f8a-a2e1-b467db754dba\",\"trace_id\":\"0d2c5c78-f780-4c85-81ff-c26ad669374b\"}\n[2026-05-11 11:06:30] local.INFO: Running pre-meeting notification command {\"correlation_id\":\"4fa18424-38b1-4f8a-a2e1-b467db754dba\",\"trace_id\":\"0d2c5c78-f780-4c85-81ff-c26ad669374b\"}\n[2026-05-11 11:06:30] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage for command {\"command\":\"conference:pre-meeting-notification\",\"memoryBeforeCommandInMb\":60.0,\"memoryAfterCommandInMB\":60.0,\"memoryPeakBeforeCommandInMb\":99.727,\"memoryPeakAfterCommandInMB\":99.727} {\"correlation_id\":\"4fa18424-38b1-4f8a-a2e1-b467db754dba\",\"trace_id\":\"0d2c5c78-f780-4c85-81ff-c26ad669374b\"}\n[2026-05-11 11:06:31] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"conference:monitor:start\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.727} {\"correlation_id\":\"ad10418d-4623-4ff3-b748-2e19330019b3\",\"trace_id\":\"a31c4682-7665-42af-99bd-8ab71b6cd9ac\"}\n[2026-05-11 11:06:31] local.INFO: Running conference:monitor:start command for activities in (2026-05-11 10:56:00, 2026-05-11 11:01:00] {\"correlation_id\":\"ad10418d-4623-4ff3-b748-2e19330019b3\",\"trace_id\":\"a31c4682-7665-42af-99bd-8ab71b6cd9ac\"}\n[2026-05-11 11:06:31] local.INFO: [conference:monitor:start] start ok {\"activity_id\":484852} {\"correlation_id\":\"ad10418d-4623-4ff3-b748-2e19330019b3\",\"trace_id\":\"a31c4682-7665-42af-99bd-8ab71b6cd9ac\"}\n[2026-05-11 11:06:31] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage for command {\"command\":\"conference:monitor:start\",\"memoryBeforeCommandInMb\":60.0,\"memoryAfterCommandInMB\":60.0,\"memoryPeakBeforeCommandInMb\":99.727,\"memoryPeakAfterCommandInMB\":99.727} {\"correlation_id\":\"ad10418d-4623-4ff3-b748-2e19330019b3\",\"trace_id\":\"a31c4682-7665-42af-99bd-8ab71b6cd9ac\"}\n[2026-05-11 11:06:33] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"conference:monitor:end\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.727} {\"correlation_id\":\"31a09cc3-a94d-4145-ab23-b041b077106a\",\"trace_id\":\"82ffd281-4725-402c-90c7-162549d4415a\"}\n[2026-05-11 11:06:33] local.INFO: conference:monitor:end:Jiminny\\Console\\Commands\\Activities\\MonitorMeetingEndCommand::logActivitiesEnded {\"from\":\"11:01\",\"to\":\"11:06\"} {\"correlation_id\":\"31a09cc3-a94d-4145-ab23-b041b077106a\",\"trace_id\":\"82ffd281-4725-402c-90c7-162549d4415a\"}\n[2026-05-11 11:06:33] local.INFO: conference:monitor:end:Jiminny\\Console\\Commands\\Activities\\MonitorMeetingEndCommand::logActivitiesWithUnfinishedSession {\"from\":\"00:56\",\"to\":\"01:01\"} {\"correlation_id\":\"31a09cc3-a94d-4145-ab23-b041b077106a\",\"trace_id\":\"82ffd281-4725-402c-90c7-162549d4415a\"}\n[2026-05-11 11:06:33] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage for command {\"command\":\"conference:monitor:end\",\"memoryBeforeCommandInMb\":60.0,\"memoryAfterCommandInMB\":60.0,\"memoryPeakBeforeCommandInMb\":99.727,\"memoryPeakAfterCommandInMB\":99.727} {\"correlation_id\":\"31a09cc3-a94d-4145-ab23-b041b077106a\",\"trace_id\":\"82ffd281-4725-402c-90c7-162549d4415a\"}\n[2026-05-11 11:06:35] local.NOTICE: Repairing HubSpot tokens start {\"correlation_id\":\"b78a7ca3-5d72-4b23-8794-4543dd6d8ffc\",\"trace_id\":\"bedc11e2-b932-44b3-a16d-436f13a1254e\"}\n[2026-05-11 11:06:35] local.INFO: Trying to refresh HubSpot token {\"account_id\":59,\"updated_at\":\"2025-10-03 09:32:05\"} {\"correlation_id\":\"b78a7ca3-5d72-4b23-8794-4543dd6d8ffc\",\"trace_id\":\"bedc11e2-b932-44b3-a16d-436f13a1254e\"}\n[2026-05-11 11:06:35] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"b78a7ca3-5d72-4b23-8794-4543dd6d8ffc\",\"trace_id\":\"bedc11e2-b932-44b3-a16d-436f13a1254e\"}\n[2026-05-11 11:06:35] local.INFO: [SocialAccountService] Refreshing token from provider {\"socialAccountId\":59,\"provider\":\"hubspot\",\"refreshToken\":\"97b78f6e2cc49965c00c2492b602b02708b1392551e6b3f113fbaa48992af90b\",\"state\":\"full-refresh\"} {\"correlation_id\":\"b78a7ca3-5d72-4b23-8794-4543dd6d8ffc\",\"trace_id\":\"bedc11e2-b932-44b3-a16d-436f13a1254e\"}\n[2026-05-11 11:06:35] local.ERROR: Failed to refresh HubSpot token {\"account_id\":59,\"updated_at\":\"2025-10-03 09:32:05\",\"reason\":\"missing or invalid refresh token\",\"previous\":\"\"} {\"correlation_id\":\"b78a7ca3-5d72-4b23-8794-4543dd6d8ffc\",\"trace_id\":\"bedc11e2-b932-44b3-a16d-436f13a1254e\"}\n[2026-05-11 11:06:35] local.INFO: Trying to refresh HubSpot token {\"account_id\":306,\"updated_at\":\"2023-11-27 09:30:03\"} {\"correlation_id\":\"b78a7ca3-5d72-4b23-8794-4543dd6d8ffc\",\"trace_id\":\"bedc11e2-b932-44b3-a16d-436f13a1254e\"}\n[2026-05-11 11:06:35] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"b78a7ca3-5d72-4b23-8794-4543dd6d8ffc\",\"trace_id\":\"bedc11e2-b932-44b3-a16d-436f13a1254e\"}\n[2026-05-11 11:06:35] local.INFO: [SocialAccountService] Refreshing token from provider {\"socialAccountId\":306,\"provider\":\"hubspot\",\"refreshToken\":\"6fa6aa8cc641d131231acc3470f5c03cb3b07b2e580fb18f8acb3b1dbb72549b\",\"state\":\"full-refresh\"} {\"correlation_id\":\"b78a7ca3-5d72-4b23-8794-4543dd6d8ffc\",\"trace_id\":\"bedc11e2-b932-44b3-a16d-436f13a1254e\"}\n[2026-05-11 11:06:36] local.ERROR: Failed to refresh HubSpot token {\"account_id\":306,\"updated_at\":\"2023-11-27 09:30:03\",\"reason\":\"missing or invalid refresh token\",\"previous\":\"\"} {\"correlation_id\":\"b78a7ca3-5d72-4b23-8794-4543dd6d8ffc\",\"trace_id\":\"bedc11e2-b932-44b3-a16d-436f13a1254e\"}\n[2026-05-11 11:06:36] local.INFO: Trying to refresh HubSpot token {\"account_id\":1372,\"updated_at\":\"2025-10-02 14:47:06\"} {\"correlation_id\":\"b78a7ca3-5d72-4b23-8794-4543dd6d8ffc\",\"trace_id\":\"bedc11e2-b932-44b3-a16d-436f13a1254e\"}\n[2026-05-11 11:06:36] local.INFO: [EncryptedTokenManager] Generating access token. {\"mode\":\"legacy\"} {\"correlation_id\":\"b78a7ca3-5d72-4b23-8794-4543dd6d8ffc\",\"trace_id\":\"bedc11e2-b932-44b3-a16d-436f13a1254e\"}\n[2026-05-11 11:06:36] local.INFO: [SocialAccountService] Refreshing token from provider {\"socialAccountId\":1372,\"provider\":\"hubspot\",\"refreshToken\":\"9aa73948c761da29dce46c177cf9aee1fde483a44169ca38723f9f0597d7a8c4\",\"state\":\"full-refresh\"} {\"correlation_id\":\"b78a7ca3-5d72-4b23-8794-4543dd6d8ffc\",\"trace_id\":\"bedc11e2-b932-44b3-a16d-436f13a1254e\"}\n[2026-05-11 11:06:36] local.ERROR: Failed to refresh HubSpot token {\"account_id\":1372,\"updated_at\":\"2025-10-02 14:47:06\",\"reason\":\"missing or invalid refresh token\",\"previous\":\"\"} {\"correlation_id\":\"b78a7ca3-5d72-4b23-8794-4543dd6d8ffc\",\"trace_id\":\"bedc11e2-b932-44b3-a16d-436f13a1254e\"}\n[2026-05-11 11:06:36] local.NOTICE: Repairing HubSpot tokens end {\"total\":3,\"fixed\":0,\"failed\":3} {\"correlation_id\":\"b78a7ca3-5d72-4b23-8794-4543dd6d8ffc\",\"trace_id\":\"bedc11e2-b932-44b3-a16d-436f13a1254e\"}\n[2026-05-11 11:06:40] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"crm:bullhorn:ping\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.727} {\"correlation_id\":\"7f93fd49-8b24-4f96-a532-a4777ccf3183\",\"trace_id\":\"14797017-4a40-4a20-a018-dfa389b58256\"}\n[2026-05-11 11:06:40] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage before starting command {\"command\":\"conference:pre-meeting-reminder\",\"memoryBeforeCommandInMb\":60.0,\"memoryPeakBeforeCommandInMb\":99.727} {\"correlation_id\":\"00c329ca-e23d-4d27-974c-c9cce4940adf\",\"trace_id\":\"d29b535e-a485-4bb8-bcf4-61cc79b73da5\"}\n[2026-05-11 11:06:40] local.INFO: [HubSpot Journal Command] Starting polling service {\"correlation_id\":\"32a337c8-7047-40f4-b1f4-d3008f51e4da\",\"trace_id\":\"b65ab282-ade3-48d8-afd0-798307edc610\"}\n[2026-05-11 11:06:40] local.INFO: [HubSpot Journal Polling] Service starting {\"memory_limit\":\"256M\",\"max_execution_time\":\"0\",\"initial_memory_mb\":60.0} {\"correlation_id\":\"32a337c8-7047-40f4-b1f4-d3008f51e4da\",\"trace_id\":\"b65ab282-ade3-48d8-afd0-798307edc610\"}\n[2026-05-11 11:06:40] local.INFO: [HubSpot Journal Polling] Acquired polling lock {\"expires_at\":\"2026-05-11T11:08:40.813273Z\"} {\"correlation_id\":\"32a337c8-7047-40f4-b1f4-d3008f51e4da\",\"trace_id\":\"b65ab282-ade3-48d8-afd0-798307edc610\"}\n[2026-05-11 11:06:40] local.INFO: [HubSpot Journal Auth] Requesting new client credentials token {\"correlation_id\":\"32a337c8-7047-40f4-b1f4-d3008f51e4da\",\"trace_id\":\"b65ab282-ade3-48d8-afd0-798307edc610\"}\n[2026-05-11 11:06:40] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage for command {\"command\":\"crm:bullhorn:ping\",\"memoryBeforeCommandInMb\":60.0,\"memoryAfterCommandInMB\":60.0,\"memoryPeakBeforeCommandInMb\":99.727,\"memoryPeakAfterCommandInMB\":99.727} {\"correlation_id\":\"7f93fd49-8b24-4f96-a532-a4777ccf3183\",\"trace_id\":\"14797017-4a40-4a20-a018-dfa389b58256\"}\n[2026-05-11 11:06:40] local.INFO: Jiminny\\Console\\Commands\\Command::run Memory usage for command {\"command\":\"conference:pre-meeting-reminder\",\"memoryBeforeCommandInMb\":60.0,\"memoryAfterCommandInMB\":60.0,\"memoryPeakBeforeCommandInMb\":99.727,\"memoryPeakAfterCommandInMB\":99.727} {\"correlation_id\":\"00c329ca-e23d-4d27-974c-c9cce4940adf\",\"trace_id\":\"d29b535e-a485-4bb8-bcf4-61cc79b73da5\"}\n[2026-05-11 11:06:41] local.INFO: [HubSpot Journal Auth] Successfully obtained new access token {\"expires_in\":1800,\"cached_for\":1500} {\"correlation_id\":\"32a337c8-7047-40f4-b1f4-d3008f51e4da\",\"trace_id\":\"b65ab282-ade3-48d8-afd0-798307edc610\"}\n[2026-05-11 11:06:41] local.INFO: [HubSpot Journal Polling] No data {\"correlation_id\":\"32a337c8-7047-40f4-b1f4-d3008f51e4da\",\"trace_id\":\"b65ab282-ade3-48d8-afd0-798307edc610\"}\n[2026-05-11 11:06:46] local.INFO: [HubSpot Journal Polling] No data {\"correlation_id\":\"32a337c8-7047-40f4-b1f4-d3008f51e4da\",\"trace_id\":\"b65ab282-ade3-48d8-afd0-798307edc610\"}","role_description":"text entry area","is_enabled":true,"is_focused":true,"is_selected":false,"is_expanded":false}]...
|
2956816745739046760
|
4180792001263167613
|
click
|
accessibility
|
NULL
|
Project: faVsco.js, menu
JY-20725-handle-HS-search Project: faVsco.js, menu
JY-20725-handle-HS-search-rate-limit, menu
Start Listening for PHP Debug Connections
HandleHubspotRateLimitTest
Run 'HandleHubspotRateLimitTest'
Debug 'HandleHubspotRateLimitTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
5
133
11
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Console\Commands;
use Carbon\Carbon;
use Carbon\CarbonImmutable;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Redis;
use InvalidArgumentException;
use Jiminny\Jobs\AutomatedReports\RequestGenerateAskJiminnyReportJob;
use Jiminny\Jobs\AutomatedReports\SendReportMailJob;
use Jiminny\Jobs\Crm\Delete\VerifyActivityCrmTaskJob;
use Jiminny\Jobs\Crm\MatchActivityCrmData;
use Jiminny\Jobs\JobDispatcherInterface;
use Jiminny\Models\Activity;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\AutomatedReportResult;
use Jiminny\Models\Team;
use Jiminny\Models\User;
use Jiminny\Repositories\AutomatedReportsRepository;
use Jiminny\Services\Activity\CrmOwnerResolver;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
/**
* Class JiminnyDebugCommand
*
* @package Jiminny\Console\Commands
*/
class JiminnyDebugCommand extends Command
{
public const string FREQUENCY_DAILY = 'daily';
public const string FREQUENCY_WEEKLY = 'weekly';
public const string FREQUENCY_MONTHLY = 'monthly';
public const string FREQUENCY_QUARTERLY = 'quarterly';
public const string FREQUENCY_ONE_OFF = 'one_off';
protected $signature = 'jiminny:debug';
public function handle(
JobDispatcherInterface $jobDispatcher,
AutomatedReportsService $automatedReportsService,
AutomatedReportsRepository $automatedReportsRepository,
UserPilotClient $userPilotClient
): void {
// Choose ONE of the following to run, then comment out the others.
// 1) Dispatch a storm of MatchActivityCrmData jobs against team 2
$this->simulateMatchActivityStorm(teamId: 2, count: 100);
// 2) Dispatch a storm of VerifyActivityCrmTaskJob jobs (simulates DeleteCrmEntityTrait fan-out)
// $this->simulateVerifyTaskStorm(teamId: 2, count: 100);
// 3) Inspect Redis circuit-breaker state for the team's HubSpot portal
// $this->observeRateLimitCache(teamId: 2);
// 4) Make 3 synchronous matchByName calls (foreground, hits API directly)
// $this->rateLimit();
exit(1);
$report = AutomatedReport::find(71);
$last = AutomatedReportResult::query()
->where('report_id', $report->getId())
->whereIn('status', [AutomatedReportResult::STATUS_DEFAULT, AutomatedReportResult::STATUS_FAILED])
// ->where('reason', '!=', AutomatedReportResult::REASON_NOT_ENOUGH_ACTIVITIES)
->whereDate('created_at', CarbonImmutable::now()->toDateString())
->latest()
->first();
$this->info("Last: {$last->getId()}");
exit(1);
$user = User::find(143);
// $count = $automatedReportsRepository->countUserReports($user);
// $this->info("Count: {$count}");
// $count = $automatedReportsRepository->countAllUserReports($user);
// $this->info("All count: {$count}");
$payload = [
'report_type' => 'ask_jiminny',
'frequency' => 'weekly',
];
$userPilotClient->track($user, 'ask-jiminny-report-generated', $payload);
exit(1);
$now = Carbon::now()->subDay(1);
$this->info("Now: {$now->toDateTimeString()}");
$weekStart = Carbon::getWeekStartsAt();
$this->info("Now: {$weekStart}");
// $from = $now->copy()->previousWeekday()->startOfDay();
// $to = $now->copy()->previousWeekday()->endOfDay();
// $fromOld = $now->copy()->subWeeks(1)->startOfDay();
// $toOld = $now->copy()->subDay()->endOfDay();
// $fromNew = $now->copy()->subWeek()->startOfWeek();
// $toNew = $now->copy()->subWeek()->endOfWeek();
// $fromOld = $now->copy()->subMonths(1)->startOfDay();
// $toOld = $now->copy()->subDay()->endOfDay();
// $fromNew = $now->copy()->subMonthNoOverflow()->startOfMonth();
// $toNew = $now->copy()->subMonthNoOverflow()->endOfMonth();
$fromOld = $now->copy()->subMonths(3)->startOfDay();
$toOld = $now->copy()->subDay()->endOfDay();
$fromNew = $now->copy()->subQuarterNoOverflow()->startOfQuarter();
$toNew = $now->copy()->subQuarterNoOverflow()->endOfQuarter();
$this->info("From old: {$fromOld->toDateTimeString()}");
$this->info("To old: {$toOld->toDateTimeString()}");
$this->info("From new: {$fromNew->toDateTimeString()}");
$this->info("To new: {$toNew->toDateTimeString()}");
exit(1);
$report = AutomatedReport::find(71);
$job = new RequestGenerateAskJiminnyReportJob($report->getUuid());
$jobDispatcher->dispatch($job);
exit(1);
// $this->formatDate($jobDispatcher);
// $this->sendMail($jobDispatcher, $automatedReportsService);
// $this->crmService();
$this->getPayload($automatedReportsService);
exit(1);
}
private function crmService()
{
$activity = Activity::find(418141);
$team = Team::find(19);
$config = $team->getCrmConfiguration();
$crmResolver = app(CrmOwnerResolver::class, [
'team' => $team,
'integrationAdmin' => $team->getOwner(),
'providerSlug' => $config->getProviderName(),
]);
$crmService = $crmResolver->prepareCrmService();
$crmService->createTranscriptNotes($activity);
}
private function sendMail(JobDispatcherInterface $jobDispatcher, AutomatedReportsService $automatedReportsService)
{
$reportUuid = '';
// $report = $automatedReportsService->getReportResult($reportUuid);
$report = AutomatedReportResult::find(275);
$validRecipients = $automatedReportsService->getValidRecipientUsers(
$report->getReport(),
includeJiminny: true,
);
$recipient = $validRecipients[0];
$fileName = $automatedReportsService->getReportFileName($report);
$typeName = $report->getReport()->getCustomName()
?? $automatedReportsService->getReportTypeName($report);
$teamsName = $automatedReportsService->getReportTeamsName($report);
$periodName = $automatedReportsService->getReportPeriodName($report);
$s3Path = $automatedReportsService->getMediaPath($report);
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$fileName ' . PHP_EOL . print_r($fileName, true));
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$typeName ' . PHP_EOL . print_r($typeName, true));
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$teamsName ' . PHP_EOL . print_r($teamsName, true));
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$periodName ' . PHP_EOL . print_r($periodName, true));
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$s3Path ' . PHP_EOL . print_r($s3Path, true));
$jobDispatcher->dispatch(
new SendReportMailJob(
reportUuid: $report->getUuid(),
s3Path: $s3Path,
recipientEmail: $recipient['email'],
recipientName: $recipient['name'] ?? null,
fileName: $fileName,
typeName: $typeName,
teamsName: $teamsName,
periodName: $periodName,
isAskJiminny: true,
)
);
exit(1);
}
private function formatDate(JobDispatcherInterface $jobDispatcher): void
{
$customName = 'Custom report name';
// $frequency = self::FREQUENCY_DAILY;
// $frequency = self::FREQUENCY_WEEKLY;
$frequency = self::FREQUENCY_MONTHLY;
// $frequency = self::FREQUENCY_QUARTERLY;
// $frequency = self::FREQUENCY_ONE_OFF;
$period = $this->calculateFromAndToDatePeriod($frequency);
$from = $period['fromDate'];
$to = $period['toDate'];
$periodName = $this->formatReportPeriodName($frequency, $from, $to);
$filenameSuffix = null;
if ($customName) {
if ($filenameSuffix) {
$customName .= " {$filenameSuffix}";
}
$result = $this->sanitizeFileName("{$customName} - {$periodName}");
}
$this->info($result);
}
public function calculateFromAndToDatePeriod(
string $frequency,
?Carbon $fromDate = null,
?Carbon $toDate = null
): array {
if ($frequency === self::FREQUENCY_ONE_OFF) {
return [
'fromDate' => $fromDate,
'toDate' => $toDate,
];
}
$now = Carbon::now();
return match ($frequency) {
self::FREQUENCY_DAILY => [
'fromDate' => $now->copy()->subDay()->startOfDay(),
'toDate' => $now->copy()->subDay()->endOfDay(),
],
self::FREQUENCY_WEEKLY => [
'fromDate' => $now->copy()->subWeeks(1)->startOfDay(),
'toDate' => $now->copy()->subDay()->endOfDay(),
],
self::FREQUENCY_MONTHLY => [
'fromDate' => $now->copy()->subMonths(1)->startOfDay(),
'toDate' => $now->copy()->subDay()->endOfDay(),
],
self::FREQUENCY_QUARTERLY => [
'fromDate' => $now->copy()->subMonths(3)->startOfDay(),
'toDate' => $now->copy()->subDay()->endOfDay(),
],
default => throw new InvalidArgumentException("Unsupported frequency: {$frequency}"),
};
}
private function formatReportPeriodName(string $frequency, Carbon $from, Carbon $to): string
{
$fromYear = $from->format('Y');
$toYear = $to->format('Y');
$differentYears = $fromYear !== $toYear;
switch ($frequency) {
case self::FREQUENCY_DAILY:
return $from->format('j M Y');
case self::FREQUENCY_QUARTERLY:
// 'Jan-Mar 2025' or 'Nov 2024-Jan 2025' if years differ
$startMonth = $from->format('M');
$endMonth = $to->copy()->subMonth();
$endMonthName = $endMonth->format('M');
$endMonthYear = $endMonth->format('Y');
if ($differentYears) {
return "{$startMonth} {$fromYear} - {$endMonthName} {$endMonthYear}";
}
return "{$startMonth} - {$endMonthName} {$toYear}";
case self::FREQUENCY_MONTHLY:
// 'May 2025' - monthly reports are always within the same year
return $from->format('M Y');
case self::FREQUENCY_WEEKLY:
// '4 - 8 Aug 2025', '27 Oct - 3 Nov 2025', or '28 Dec 2024 - 3 Jan 2025' if years differ
$startDay = $from->format('j');
$endDay = $to->format('j');
$startMonth = $from->format('M');
$endMonth = $to->format('M');
if ($differentYears) {
return "{$startDay} {$startMonth} {$fromYear} - {$endDay} {$endMonth} {$toYear}";
}
if ($startMonth !== $endMonth) {
return "{$startDay} {$startMonth} - {$endDay} {$endMonth} {$toYear}";
}
return "{$startDay} - {$endDay} {$endMonth} {$toYear}";
case self::FREQUENCY_ONE_OFF:
// '2 May-31 May 2025' or '15 Dec 2024-15 Jan 2025' if years differ
$startDay = $from->format('j');
$startMonth = $from->format('M');
$endDay = $to->format('j');
$endMonth = $to->format('M');
// If same month and year, use a format like '2-31 May 2025'
if ($startMonth === $endMonth && ! $differentYears) {
return "{$startDay} - {$endDay} {$startMonth} {$toYear}";
}
// If different years, include both years
if ($differentYears) {
return "{$startDay} {$startMonth} {$fromYear} - {$endDay} {$endMonth} {$toYear}";
}
// Same year but different months
return "{$startDay} {$startMonth} - {$endDay} {$endMonth} {$toYear}";
default:
// Default format for unknown frequencies
return $from->format('j M Y') . ' - ' . $to->format('j M Y');
}
}
public function sanitizeFileName(string $fileName): string
{
return str_replace(['/', '\\'], '-', $fileName);
}
private function getPayload(AutomatedReportsService $automatedReportsService)
{
$reportResult = AutomatedReportResult::find(269);
$automatedReport = $reportResult->getReport();
$activityIds = [1,2,3];
$payload = $automatedReportsService->getAskJiminnyGenerateReportPayload(
automatedReport: $automatedReport,
reportResult: $reportResult,
activityIds: $activityIds,
);
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$payload ' . PHP_EOL . print_r($payload, true));
}
private function rateLimit()
{
$team = Team::find(2);
$config = $team->getCrmConfiguration();
$crmResolver = app(CrmOwnerResolver::class, [
'team' => $team,
'integrationAdmin' => $team->getOwner(),
'providerSlug' => $config->getProviderName(),
]);
$crmService = $crmResolver->prepareCrmService();
for ($i = 0 ; $i < 3; $i++) {
// if ($i % 25 === 0) {
// $this->info("Syncing opportunity {$i}");
$this->info("Matching contact {$i}");
// }
// $crmService->syncOpportunity('374720564');
$crmService->matchByName('Robot');
}
}
private function simulateMatchActivityStorm(int $teamId = 2, int $count = 100): void
{
$team = Team::find($teamId);
$config = $team->getCrmConfiguration();
$activities = Activity::query()
->where('crm_configuration_id', $config->getId())
->orderByDesc('id')
->limit($count)
->get();
$this->info("Dispatching {$activities->count()} MatchActivityCrmData jobs (portal={$config->getId()})");
foreach ($activities as $activity) {
MatchActivityCrmData::dispatch($activity->getId(), $config, true);
}
$this->info('Done. Watch logs and run jiminny:debug observeRateLimit to inspect cache state.');
}
private function simulateVerifyTaskStorm(int $teamId = 2, int $count = 100): void
{
$activities = Activity::query()
->where('team_id', $teamId)
->whereNotNull('crm_provider_id')
->orderByDesc('id')
->limit($count)
->get();
$this->info("Dispatching {$activities->count()} VerifyActivityCrmTaskJob jobs");
foreach ($activities as $activity) {
VerifyActivityCrmTaskJob::dispatch($activity->getId());
}
$this->info('Done.');
}
private function observeRateLimitCache(int $teamId = 2): void
{
$team = Team::find($teamId);
$config = $team->getCrmConfiguration();
$key = sprintf('hubspot:ratelimit:portal:%d', $config->getId());
$value = Redis::get($key);
$ttl = Redis::ttl($key);
$this->info("Redis key: {$key}");
$this->info('Value: ' . ($value ?? '(empty)'));
$this->info("TTL: {$ttl}s");
}
}
Show Replace Field
Search History
429
New Line
Match Case
Words
Regex
Replace History
Replace
New Line
Preserve case
2/2
Previous Occurrence
Next Occurrence
Filter Search Results
Open in Window, Multiple Cursors
Click to highlight
Close
Sync Changes
Hide This Notification
Code changed:
Hide
174
Previous Highlighted Error
Next Highlighted Error
[2026-05-11 11:06:08] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {"activity_id":612562,"team_id":2,"email":"[EMAIL]"} {"correlation_id":"c1949c5e-c0f4-431e-ba26-59d49408d871","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:08] local.INFO: [CrmActivityService] CRM matching completed {"activity_id":612562,"participants_processed":2,"exact_matches":0,"domain_matches":1,"best_match_found":true} {"correlation_id":"c1949c5e-c0f4-431e-ba26-59d49408d871","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:08] local.INFO: [ EsUpdateTarget ] Update single target {"target":"activities","purpose":"searchable-observer-update","entityId":612562} {"correlation_id":"c1949c5e-c0f4-431e-ba26-59d49408d871","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:08] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {"entityType":"activities","entityId":612562,"collectionKey":"activities-for-update-priority","withPriority":true} {"correlation_id":"c1949c5e-c0f4-431e-ba26-59d49408d871","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:08] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {"activity":612562,"remote_search":true,"lead_id":null,"contact_id":null,"account_id":26,"opportunity_id":22,"stage_id":89} {"correlation_id":"c1949c5e-c0f4-431e-ba26-59d49408d871","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:09] local.INFO: [MatchActivityCrmData] Starting CRM data matching {"activity":612561,"remote_search":true,"set_configuration":2,"old_state":{"lead_id":null,"contact_id":null,"account_id":26,"opportunity_id":22,"stage_id":89}} {"correlation_id":"b715f531-04da-410f-be31-055aa1c13754","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:09] local.INFO: [ EsUpdateTarget ] Update single target {"target":"activities","purpose":"searchable-observer-update","entityId":612561} {"correlation_id":"b715f531-04da-410f-be31-055aa1c13754","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:09] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {"entityType":"activities","entityId":612561,"collectionKey":"activities-for-update-priority","withPriority":true} {"correlation_id":"b715f531-04da-410f-be31-055aa1c13754","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:09] local.INFO: [MatchActivityCrmData] Participants old state {"activity":612561,"participants":[{"id":999780,"user_id":206,"contact_id":null,"lead_id":null},{"id":999781,"user_id":null,"contact_id":null,"lead_id":null}]} {"correlation_id":"b715f531-04da-410f-be31-055aa1c13754","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:09] local.INFO: [SocialAccountService] Fetching token {"socialAccountId":1499,"provider":"hubspot"} {"correlation_id":"b715f531-04da-410f-be31-055aa1c13754","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:09] local.INFO: [SocialAccountService] Token retrieved {"socialAccountId":1499,"provider":"hubspot"} {"correlation_id":"b715f531-04da-410f-be31-055aa1c13754","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:09] local.INFO: [EncryptedTokenManager] Generating access token. {"mode":"legacy"} {"correlation_id":"b715f531-04da-410f-be31-055aa1c13754","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:09] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {"crm_provider":"hubspot","crm_owner":148,"team_id":2} {"correlation_id":"b715f531-04da-410f-be31-055aa1c13754","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:09] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {"activity_id":612561,"team_id":2,"email":"[EMAIL]"} {"correlation_id":"b715f531-04da-410f-be31-055aa1c13754","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:09] local.INFO: [Prospect match] Cache miss, calling the API {"identifier_type":"email","identifier":"[EMAIL]"} {"correlation_id":"b715f531-04da-410f-be31-055aa1c13754","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:10] local.INFO: [Hubspot] Failed to fetch contact {"email":"[EMAIL]","reason":"[404] Client error: `GET https://api.hubapi.com/crm/v3/objects/contacts/447700174614.447782589921.OeREojLVnk%40txt.staging.jiminny.com?properties=email%2Cfirstname%2Clastname%2Ccountry%2Cphone%2Cmobilephone%2Cjobtitle%2Chubspot_owner_id%2Cassociatedcompanyid%2Cphoto&archived=0&idProperty=email` resulted in a `404 Not Found` response"} {"correlation_id":"b715f531-04da-410f-be31-055aa1c13754","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:10] local.INFO: [Prospect match] API returned empty result, caching the miss with empty prospect data {"identifier_type":"email","identifier":"[EMAIL]"} {"correlation_id":"b715f531-04da-410f-be31-055aa1c13754","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:10] local.INFO: [Prospect match] Resolved company domain from email {"email":"[EMAIL]","domain":"jiminny.com"} {"correlation_id":"b715f531-04da-410f-be31-055aa1c13754","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:10] local.INFO: [CrmActivityService] CRM matching completed {"activity_id":612561,"participants_processed":2,"exact_matches":0,"domain_matches":1,"best_match_found":true} {"correlation_id":"b715f531-04da-410f-be31-055aa1c13754","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:10] local.INFO: [ EsUpdateTarget ] Update single target {"target":"activities","purpose":"searchable-observer-update","entityId":612561} {"correlation_id":"b715f531-04da-410f-be31-055aa1c13754","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:10] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {"entityType":"activities","entityId":612561,"collectionKey":"activities-for-update-priority","withPriority":true} {"correlation_id":"b715f531-04da-410f-be31-055aa1c13754","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:10] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {"activity":612561,"remote_search":true,"lead_id":null,"contact_id":null,"account_id":26,"opportunity_id":22,"stage_id":89} {"correlation_id":"b715f531-04da-410f-be31-055aa1c13754","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:10] local.INFO: [MatchActivityCrmData] Starting CRM data matching {"activity":612560,"remote_search":true,"set_configuration":2,"old_state":{"lead_id":null,"contact_id":null,"account_id":26,"opportunity_id":22,"stage_id":89}} {"correlation_id":"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:10] local.INFO: [ EsUpdateTarget ] Update single target {"target":"activities","purpose":"searchable-observer-update","entityId":612560} {"correlation_id":"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:10] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {"entityType":"activities","entityId":612560,"collectionKey":"activities-for-update-priority","withPriority":true} {"correlation_id":"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:10] local.INFO: [MatchActivityCrmData] Participants old state {"activity":612560,"participants":[{"id":999778,"user_id":null,"contact_id":null,"lead_id":null},{"id":999779,"user_id":206,"contact_id":null,"lead_id":null}]} {"correlation_id":"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [SocialAccountService] Fetching token {"socialAccountId":1499,"provider":"hubspot"} {"correlation_id":"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [SocialAccountService] Token retrieved {"socialAccountId":1499,"provider":"hubspot"} {"correlation_id":"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [EncryptedTokenManager] Generating access token. {"mode":"legacy"} {"correlation_id":"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {"crm_provider":"hubspot","crm_owner":148,"team_id":2} {"correlation_id":"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [Prospect match] Cache / local search hit {"identifier_type":"email","identifier":"[EMAIL]"} {"correlation_id":"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [Prospect match] cached empty result - no API calls, try next matching method {"identifier_type":"email","identifier":"[EMAIL]"} {"correlation_id":"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [Prospect match] Resolved company domain from email {"email":"[EMAIL]","domain":"jiminny.com"} {"correlation_id":"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {"activity_id":612560,"team_id":2,"email":"[EMAIL]"} {"correlation_id":"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [CrmActivityService] CRM matching completed {"activity_id":612560,"participants_processed":2,"exact_matches":0,"domain_matches":1,"best_match_found":true} {"correlation_id":"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [ EsUpdateTarget ] Update single target {"target":"activities","purpose":"searchable-observer-update","entityId":612560} {"correlation_id":"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [ EsUpdateProcessManager ] Finished updating entities in ES {"worker":"","peak_memory":"99.73 MB","elapsed_seconds":0.74,"update_target":"activities","should_iterate_again":false} {"correlation_id":"38935ae1-fcb3-437e-9c9e-1b7415a0b7e1","trace_id":"325a2f9b-9c66-4223-ac02-1a43723f4315"}
[2026-05-11 11:06:11] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {"entityType":"activities","entityId":612560,"collectionKey":"activities-for-update-priority","withPriority":true} {"correlation_id":"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {"activity":612560,"remote_search":true,"lead_id":null,"contact_id":null,"account_id":26,"opportunity_id":22,"stage_id":89} {"correlation_id":"cccc5254-c830-4ccb-bcf2-fa10b0e5abe1","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [MatchActivityCrmData] Starting CRM data matching {"activity":612559,"remote_search":true,"set_configuration":2,"old_state":{"lead_id":null,"contact_id":97,"account_id":69,"opportunity_id":5011,"stage_id":34}} {"correlation_id":"4d59a59c-38a5-4a1d-9379-6ceaf67b747e","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [ EsUpdateTarget ] Update single target {"target":"activities","purpose":"searchable-observer-update","entityId":612559} {"correlation_id":"4d59a59c-38a5-4a1d-9379-6ceaf67b747e","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {"entityType":"activities","entityId":612559,"collectionKey":"activities-for-update-priority","withPriority":true} {"correlation_id":"4d59a59c-38a5-4a1d-9379-6ceaf67b747e","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [MatchActivityCrmData] Participants old state {"activity":612559,"participants":[{"id":999776,"user_id":206,"contact_id":null,"lead_id":null},{"id":999777,"user_id":null,"contact_id":97,"lead_id":null}]} {"correlation_id":"4d59a59c-38a5-4a1d-9379-6ceaf67b747e","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [SocialAccountService] Fetching token {"socialAccountId":1499,"provider":"hubspot"} {"correlation_id":"4d59a59c-38a5-4a1d-9379-6ceaf67b747e","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [SocialAccountService] Token retrieved {"socialAccountId":1499,"provider":"hubspot"} {"correlation_id":"4d59a59c-38a5-4a1d-9379-6ceaf67b747e","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [EncryptedTokenManager] Generating access token. {"mode":"legacy"} {"correlation_id":"4d59a59c-38a5-4a1d-9379-6ceaf67b747e","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {"crm_provider":"hubspot","crm_owner":148,"team_id":2} {"correlation_id":"4d59a59c-38a5-4a1d-9379-6ceaf67b747e","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {"activity_id":612559,"team_id":2,"email":"[EMAIL]"} {"correlation_id":"4d59a59c-38a5-4a1d-9379-6ceaf67b747e","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: ProspectCache - Searching DB for opportunity by owner {"account_id":69,"contact_id":97,"owner_id":206} {"correlation_id":"4d59a59c-38a5-4a1d-9379-6ceaf67b747e","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: ProspectCache - Fallback DB opportunity search {"account_id":69,"contact_id":97} {"correlation_id":"4d59a59c-38a5-4a1d-9379-6ceaf67b747e","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: ProspectCache - Opportunity DB search results {"account_id":69,"contact_id":97,"opportunity_id":5011} {"correlation_id":"4d59a59c-38a5-4a1d-9379-6ceaf67b747e","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [Prospect match] Cache / local search hit {"identifier_type":"email","identifier":"[EMAIL]"} {"correlation_id":"4d59a59c-38a5-4a1d-9379-6ceaf67b747e","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [CrmActivityService] CRM matching completed {"activity_id":612559,"participants_processed":2,"exact_matches":1,"domain_matches":0,"best_match_found":true} {"correlation_id":"4d59a59c-38a5-4a1d-9379-6ceaf67b747e","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [ EsUpdateTarget ] Update single target {"target":"activities","purpose":"searchable-observer-update","entityId":612559} {"correlation_id":"4d59a59c-38a5-4a1d-9379-6ceaf67b747e","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {"entityType":"activities","entityId":612559,"collectionKey":"activities-for-update-priority","withPriority":true} {"correlation_id":"4d59a59c-38a5-4a1d-9379-6ceaf67b747e","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:11] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {"activity":612559,"remote_search":true,"lead_id":null,"contact_id":97,"account_id":69,"opportunity_id":5011,"stage_id":34} {"correlation_id":"4d59a59c-38a5-4a1d-9379-6ceaf67b747e","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:12] local.INFO: [MatchActivityCrmData] Starting CRM data matching {"activity":612558,"remote_search":true,"set_configuration":2,"old_state":{"lead_id":null,"contact_id":97,"account_id":69,"opportunity_id":5011,"stage_id":34}} {"correlation_id":"a105800d-d2f7-4772-a534-7791e262461c","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:12] local.INFO: [ EsUpdateTarget ] Update single target {"target":"activities","purpose":"searchable-observer-update","entityId":612558} {"correlation_id":"a105800d-d2f7-4772-a534-7791e262461c","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:12] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {"entityType":"activities","entityId":612558,"collectionKey":"activities-for-update-priority","withPriority":true} {"correlation_id":"a105800d-d2f7-4772-a534-7791e262461c","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:12] local.INFO: [MatchActivityCrmData] Participants old state {"activity":612558,"participants":[{"id":999774,"user_id":206,"contact_id":null,"lead_id":null},{"id":999775,"user_id":null,"contact_id":97,"lead_id":null}]} {"correlation_id":"a105800d-d2f7-4772-a534-7791e262461c","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:12] local.INFO: [SocialAccountService] Fetching token {"socialAccountId":1499,"provider":"hubspot"} {"correlation_id":"a105800d-d2f7-4772-a534-7791e262461c","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:12] local.INFO: [SocialAccountService] Token retrieved {"socialAccountId":1499,"provider":"hubspot"} {"correlation_id":"a105800d-d2f7-4772-a534-7791e262461c","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:12] local.INFO: [EncryptedTokenManager] Generating access token. {"mode":"legacy"} {"correlation_id":"a105800d-d2f7-4772-a534-7791e262461c","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:12] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {"crm_provider":"hubspot","crm_owner":148,"team_id":2} {"correlation_id":"a105800d-d2f7-4772-a534-7791e262461c","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:12] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {"activity_id":612558,"team_id":2,"email":"[EMAIL]"} {"correlation_id":"a105800d-d2f7-4772-a534-7791e262461c","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:12] local.INFO: [Prospect match] Cache / local search hit {"identifier_type":"email","identifier":"[EMAIL]"} {"correlation_id":"a105800d-d2f7-4772-a534-7791e262461c","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:12] local.INFO: [CrmActivityService] CRM matching completed {"activity_id":612558,"participants_processed":2,"exact_matches":1,"domain_matches":0,"best_match_found":true} {"correlation_id":"a105800d-d2f7-4772-a534-7791e262461c","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:12] local.INFO: [ EsUpdateTarget ] Update single target {"target":"activities","purpose":"searchable-observer-update","entityId":612558} {"correlation_id":"a105800d-d2f7-4772-a534-7791e262461c","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:12] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {"entityType":"activities","entityId":612558,"collectionKey":"activities-for-update-priority","withPriority":true} {"correlation_id":"a105800d-d2f7-4772-a534-7791e262461c","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:12] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {"activity":612558,"remote_search":true,"lead_id":null,"contact_id":97,"account_id":69,"opportunity_id":5011,"stage_id":34} {"correlation_id":"a105800d-d2f7-4772-a534-7791e262461c","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:13] local.INFO: [MatchActivityCrmData] Starting CRM data matching {"activity":612557,"remote_search":true,"set_configuration":2,"old_state":{"lead_id":null,"contact_id":97,"account_id":69,"opportunity_id":5011,"stage_id":34}} {"correlation_id":"3ec57437-5796-4e44-8bf0-aaec149ac813","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:13] local.INFO: [ EsUpdateTarget ] Update single target {"target":"activities","purpose":"searchable-observer-update","entityId":612557} {"correlation_id":"3ec57437-5796-4e44-8bf0-aaec149ac813","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:13] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {"entityType":"activities","entityId":612557,"collectionKey":"activities-for-update-priority","withPriority":true} {"correlation_id":"3ec57437-5796-4e44-8bf0-aaec149ac813","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:13] local.INFO: [MatchActivityCrmData] Participants old state {"activity":612557,"participants":[{"id":999772,"user_id":206,"contact_id":null,"lead_id":null},{"id":999773,"user_id":null,"contact_id":97,"lead_id":null}]} {"correlation_id":"3ec57437-5796-4e44-8bf0-aaec149ac813","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:13] local.INFO: Jiminny\Console\Commands\Command::run Memory usage before starting command {"command":"mailbox:skip-lists:refresh","memoryBeforeCommandInMb":60.0,"memoryPeakBeforeCommandInMb":99.727} {"correlation_id":"5203040f-23be-43e6-8410-ba49ee1b38b4","trace_id":"e787953d-9b78-4b63-a987-4f93f6892984"}
[2026-05-11 11:06:13] local.INFO: [SocialAccountService] Fetching token {"socialAccountId":1499,"provider":"hubspot"} {"correlation_id":"3ec57437-5796-4e44-8bf0-aaec149ac813","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:13] local.INFO: [SocialAccountService] Token retrieved {"socialAccountId":1499,"provider":"hubspot"} {"correlation_id":"3ec57437-5796-4e44-8bf0-aaec149ac813","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:13] local.INFO: [EncryptedTokenManager] Generating access token. {"mode":"legacy"} {"correlation_id":"3ec57437-5796-4e44-8bf0-aaec149ac813","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:13] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {"crm_provider":"hubspot","crm_owner":148,"team_id":2} {"correlation_id":"3ec57437-5796-4e44-8bf0-aaec149ac813","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:13] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {"activity_id":612557,"team_id":2,"email":"[EMAIL]"} {"correlation_id":"3ec57437-5796-4e44-8bf0-aaec149ac813","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:13] local.INFO: [Prospect match] Cache / local search hit {"identifier_type":"email","identifier":"[EMAIL]"} {"correlation_id":"3ec57437-5796-4e44-8bf0-aaec149ac813","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:13] local.INFO: [CrmActivityService] CRM matching completed {"activity_id":612557,"participants_processed":2,"exact_matches":1,"domain_matches":0,"best_match_found":true} {"correlation_id":"3ec57437-5796-4e44-8bf0-aaec149ac813","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:13] local.INFO: [ EsUpdateTarget ] Update single target {"target":"activities","purpose":"searchable-observer-update","entityId":612557} {"correlation_id":"3ec57437-5796-4e44-8bf0-aaec149ac813","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:13] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {"entityType":"activities","entityId":612557,"collectionKey":"activities-for-update-priority","withPriority":true} {"correlation_id":"3ec57437-5796-4e44-8bf0-aaec149ac813","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:13] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {"activity":612557,"remote_search":true,"lead_id":null,"contact_id":97,"account_id":69,"opportunity_id":5011,"stage_id":34} {"correlation_id":"3ec57437-5796-4e44-8bf0-aaec149ac813","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:13] local.INFO: Jiminny\Console\Commands\Command::run Memory usage for command {"command":"mailbox:skip-lists:refresh","memoryBeforeCommandInMb":60.0,"memoryAfterCommandInMB":60.0,"memoryPeakBeforeCommandInMb":99.727,"memoryPeakAfterCommandInMB":99.727} {"correlation_id":"5203040f-23be-43e6-8410-ba49ee1b38b4","trace_id":"e787953d-9b78-4b63-a987-4f93f6892984"}
[2026-05-11 11:06:13] local.INFO: [MatchActivityCrmData] Starting CRM data matching {"activity":612556,"remote_search":true,"set_configuration":2,"old_state":{"lead_id":null,"contact_id":97,"account_id":69,"opportunity_id":5011,"stage_id":34}} {"correlation_id":"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:13] local.INFO: [ EsUpdateTarget ] Update single target {"target":"activities","purpose":"searchable-observer-update","entityId":612556} {"correlation_id":"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:13] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {"entityType":"activities","entityId":612556,"collectionKey":"activities-for-update-priority","withPriority":true} {"correlation_id":"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:13] local.INFO: [MatchActivityCrmData] Participants old state {"activity":612556,"participants":[{"id":999770,"user_id":206,"contact_id":null,"lead_id":null},{"id":999771,"user_id":null,"contact_id":97,"lead_id":null}]} {"correlation_id":"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:13] local.INFO: [SocialAccountService] Fetching token {"socialAccountId":1499,"provider":"hubspot"} {"correlation_id":"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:13] local.INFO: [SocialAccountService] Token retrieved {"socialAccountId":1499,"provider":"hubspot"} {"correlation_id":"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:13] local.INFO: [EncryptedTokenManager] Generating access token. {"mode":"legacy"} {"correlation_id":"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:13] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {"crm_provider":"hubspot","crm_owner":148,"team_id":2} {"correlation_id":"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:14] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {"activity_id":612556,"team_id":2,"email":"[EMAIL]"} {"correlation_id":"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:14] local.INFO: [Prospect match] Cache / local search hit {"identifier_type":"email","identifier":"[EMAIL]"} {"correlation_id":"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:14] local.INFO: [CrmActivityService] CRM matching completed {"activity_id":612556,"participants_processed":2,"exact_matches":1,"domain_matches":0,"best_match_found":true} {"correlation_id":"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:14] local.INFO: [ EsUpdateTarget ] Update single target {"target":"activities","purpose":"searchable-observer-update","entityId":612556} {"correlation_id":"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:14] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {"entityType":"activities","entityId":612556,"collectionKey":"activities-for-update-priority","withPriority":true} {"correlation_id":"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:14] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {"activity":612556,"remote_search":true,"lead_id":null,"contact_id":97,"account_id":69,"opportunity_id":5011,"stage_id":34} {"correlation_id":"fc0656ec-ee57-48ab-bbae-15c2bf6a6df6","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:14] local.INFO: [MatchActivityCrmData] Starting CRM data matching {"activity":612555,"remote_search":true,"set_configuration":2,"old_state":{"lead_id":null,"contact_id":97,"account_id":69,"opportunity_id":5011,"stage_id":34}} {"correlation_id":"3e9d1a70-5491-44ae-8f28-5cb9b60e1859","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:14] local.INFO: [ EsUpdateTarget ] Update single target {"target":"activities","purpose":"searchable-observer-update","entityId":612555} {"correlation_id":"3e9d1a70-5491-44ae-8f28-5cb9b60e1859","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:14] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {"entityType":"activities","entityId":612555,"collectionKey":"activities-for-update-priority","withPriority":true} {"correlation_id":"3e9d1a70-5491-44ae-8f28-5cb9b60e1859","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:14] local.INFO: [MatchActivityCrmData] Participants old state {"activity":612555,"participants":[{"id":999768,"user_id":206,"contact_id":null,"lead_id":null},{"id":999769,"user_id":null,"contact_id":97,"lead_id":null}]} {"correlation_id":"3e9d1a70-5491-44ae-8f28-5cb9b60e1859","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:14] local.INFO: [SocialAccountService] Fetching token {"socialAccountId":1499,"provider":"hubspot"} {"correlation_id":"3e9d1a70-5491-44ae-8f28-5cb9b60e1859","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:14] local.INFO: [SocialAccountService] Token retrieved {"socialAccountId":1499,"provider":"hubspot"} {"correlation_id":"3e9d1a70-5491-44ae-8f28-5cb9b60e1859","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:14] local.INFO: [EncryptedTokenManager] Generating access token. {"mode":"legacy"} {"correlation_id":"3e9d1a70-5491-44ae-8f28-5cb9b60e1859","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:14] local.INFO: [CrmOwnerResolver] Integration owner matched as CRM Owner {"crm_provider":"hubspot","crm_owner":148,"team_id":2} {"correlation_id":"3e9d1a70-5491-44ae-8f28-5cb9b60e1859","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:14] local.INFO: [CrmActivityService] Email domain belongs to the team, skipping crm lookup {"activity_id":612555,"team_id":2,"email":"[EMAIL]"} {"correlation_id":"3e9d1a70-5491-44ae-8f28-5cb9b60e1859","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:14] local.INFO: [Prospect match] Cache / local search hit {"identifier_type":"email","identifier":"[EMAIL]"} {"correlation_id":"3e9d1a70-5491-44ae-8f28-5cb9b60e1859","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:14] local.INFO: [CrmActivityService] CRM matching completed {"activity_id":612555,"participants_processed":2,"exact_matches":1,"domain_matches":0,"best_match_found":true} {"correlation_id":"3e9d1a70-5491-44ae-8f28-5cb9b60e1859","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:14] local.INFO: [ EsUpdateTarget ] Update single target {"target":"activities","purpose":"searchable-observer-update","entityId":612555} {"correlation_id":"3e9d1a70-5491-44ae-8f28-5cb9b60e1859","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:14] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {"entityType":"activities","entityId":612555,"collectionKey":"activities-for-update-priority","withPriority":true} {"correlation_id":"3e9d1a70-5491-44ae-8f28-5cb9b60e1859","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:14] local.INFO: [MatchActivityCrmData] Successfully matched CRM data {"activity":612555,"remote_search":true,"lead_id":null,"contact_id":97,"account_id":69,"opportunity_id":5011,"stage_id":34} {"correlation_id":"3e9d1a70-5491-44ae-8f28-5cb9b60e1859","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:14] local.INFO: [MatchActivityCrmData] Starting CRM data matching {"activity":612554,"remote_search":true,"set_configuration":2,"old_state":{"lead_id":null,"contact_id":97,"account_id":69,"opportunity_id":5011,"stage_id":34}} {"correlation_id":"7101e979-3a7e-4b2c-bf04-dc6a24ba1356","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:14] local.INFO: [ EsUpdateTarget ] Update single target {"target":"activities","purpose":"searchable-observer-update","entityId":612554} {"correlation_id":"7101e979-3a7e-4b2c-bf04-dc6a24ba1356","trace_id":"064c6959-5450-4b4b-b66f-93c5c058f5d4"}
[2026-05-11 11:06:14] local.INFO: [ AsyncUpdateElasticSearch ] Entity added to Redis list {"entityType":"activities","entityId":612554,"collectionKey":"activities-for-update-priority",...
|
NULL
|
NULL
|
NULL
|
NULL
|
|
18250
|
790
|
30
|
2026-05-11T11:06:55.828188+00:00
|
/Users/lukas/.screenpipe/data/data/2026-05-11/1778 /Users/lukas/.screenpipe/data/data/2026-05-11/1778497615828_m1.jpg...
|
PhpStorm
|
faVsco.js – laravel.log
|
1
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
JY-20725-handle-HS-search Project: faVsco.js, menu
JY-20725-handle-HS-search-rate-limit, menu
Start Listening for PHP Debug Connections
HandleHubspotRateLimitTest
Run 'HandleHubspotRateLimitTest'...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"on_screen":true,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JY-20725-handle-HS-search-rate-limit, menu","depth":5,"on_screen":true,"help_text":"Git Branch: JY-20725-handle-HS-search-rate-limit","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"HandleHubspotRateLimitTest","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'HandleHubspotRateLimitTest'","depth":6,"on_screen":true,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
-6522339035140287979
|
-8348248415050200704
|
click
|
hybrid
|
NULL
|
Project: faVsco.js, menu
JY-20725-handle-HS-search Project: faVsco.js, menu
JY-20725-handle-HS-search-rate-limit, menu
Start Listening for PHP Debug Connections
HandleHubspotRateLimitTest
Run 'HandleHubspotRateLimitTest'
SlackFileEditViewGoHistoryWindowHelp• 0DOCKERcompiledeventsroutesviewsO ₴1DEV (docker)$2APP (-zsh)883Jiminny-worker-processing-delayed: jiminny-worker-processing-delayed_00:stoppedjiminny-worker-processing-2:jiminny-worker-processing-2_00: stoppedjiminny-worker-processing-3:jiminny-worker-processing-3_00: stoppedjiminny-worker-processing-4:jiminny-worker-processing-4_00:stoppedjiminny-worker-processing-5: jiminny-worker-processing-5_00:stoppedworker-analytics:worker-analytics_00: stoppedworker-crm-update:worker-crm-update_00: stoppedworker-download:worker-download_00:stoppedworker-nudges:worker-nudges_00:stoppedjiminny-worker-processing-1:jiminny-worker-processing-1_00: stoppedworker:worker_00: stoppedworker-calendar:worker-calendar_00:worker-conferences:worker-conferences_00:worker-crm-sync:worker-crm-sync_00: stoppedworker-es-update:worker-es-update_00:worker-emails:worker-emails_00: stoppedworker-audio:worker-audio_00: stoppedartisan-schedule:artisan-schedule_00: stoppedartisan-schedule:artisan-schedule_00: startedjiminny-worker-processing-1:jiminny-worker-processing-1_00: startedjiminny-worker-processing-2:jiminny-worker-processing-2_00: startedjiminny-worker-processing-3:jiminny-worker-processing-3_00: startedjiminny-worker-processing-4:jiminny-worker-processing-4_00: startedjiminny-worker-processing-5:jiminny-worker-processing-5_00:startedjiminny-worker-processing-delayed: jiminny-worker-processing-delayed_00: startedworker:worker_00: startedworker-analytics:worker-analytics_00: startedworker-audio:worker-audio_00: startedworker-calendar:worker-calendar_00:startedworker-conferences:worker-conferences_00: startedworker-crm-sync:worker-crm-sync_00: startedworker-crm-update:worker-crm-update_00: startedworker-download:worker-download_00: startedworker-emails:worker-emails_00: startedworker-es-update:worker-es-update_00:startedworker-nudges:worker-nudges_00: startedroot@docker_lamp_1:/home/jiminny#php artisan jiminny:debugDispatching 100 MatchActivityCrmDatajobs (portal=2)Done.Watch logs and runjiminny:debug observeRateLimit to inspect cache state.root@docker_lamp_1:/home/jiminny# ]DEV (dl-zshHomeDMsActivityFilesLater...Morealo)• Preparation for Refi... in 54 m100% <78• Mon 11 May 14:06:55→Describe what you are looking forJiminny ...# contusion-clinic# curiosity_lab# engineering# general# jiminny-bg# platform-tickets# product_launches# random# releases# sofia-office# support# thank-yous# the_people_of jimi...^ Direct messagesP. Aneliya Angelova®. Galya Dimitrova& Petko Kashinski&. Stefka StoyanovaVasil Vasilev&. Nikolay IvanovAneliya Angelova, ...Stoyan Tanev• VesE Lukas Kovalik y…..::: AppsS Jira CloudToastGanala Cala# thank-yous406 0• Messagest* Add canvas+O FilesWednesday, May 6th~It shows increaiuie skir tu jump into Supportand not miss a beat.More importantly, itshows great heart and helped delight ourcustomers on two ways at the same time:Support and Trials.You embodied so many of our values duringthis period.#selflessness #lowego #customerfocus#positiveattitudeView Recognition Moment9-5SD 1011 reply 5 days agoToday ~Greg 1:31 PMShout out to engineering/infrastructure teamand their stability work. Did you know ... last weekour Product had 100% uptime? ..not99.9999999% but /2 %!lots of little things (and a few big things) make thathappen. well done all!81#1NewKara Jones 1:54 PM100!!!!!! That is amazing!!! Power team!!Message #thank-yous+...
|
NULL
|
NULL
|
NULL
|
NULL
|