commit f9b16d05a1231e4e904cfe516844fbd8cdc5d8cb Author: a2a-platform Date: Sat Jun 27 01:03:18 2026 +0000 deploy diff --git a/README.md b/README.md new file mode 100644 index 0000000..deeed30 --- /dev/null +++ b/README.md @@ -0,0 +1,11 @@ +# hubspot-contacts-openapi-agent + +Generated A2APack agent for Contacts. + +- Source OpenAPI: https://raw.githubusercontent.com/HubSpot/HubSpot-public-api-spec-collection/main/PublicApiSpecs/CRM/Contacts/Rollouts/424/v3/contacts.json +- Generated operations: 13 +- Main skill: `auto` +- Routing mode: direct operation skills + +The generated code is intentionally editable. Tune prompts, operation +grouping, auth names, and safety policy before publishing serious agents. diff --git a/a2a.yaml b/a2a.yaml new file mode 100644 index 0000000..f1091ca --- /dev/null +++ b/a2a.yaml @@ -0,0 +1,13 @@ +name: hubspot-contacts-openapi-agent +version: v3 +entrypoint: agent:HubspotContactsOpenapiAgent +description: CRM object API for creating, reading, searching, and batching HubSpot + contacts. +runtime: + resources: + cpu: 200m + memory: 512Mi + egress: + allow_hosts: + - api.hubapi.com + deny_internet_by_default: true diff --git a/agent.py b/agent.py new file mode 100644 index 0000000..8a208c8 --- /dev/null +++ b/agent.py @@ -0,0 +1,678 @@ +from __future__ import annotations + +import base64 +import json +import re +from pathlib import Path +from typing import Any + +import httpx +from a2a_pack.deepagents import create_a2a_deep_agent +from langchain_core.messages import BaseMessage +from langchain_core.tools import StructuredTool +from pydantic import BaseModel, Field + +from a2a_pack import ( + A2AAgent, + ConsumerSetup, + ConsumerSetupField, + ConsumerSetupMissing, + EgressPolicy, + LLMProvisioning, + Pricing, + Resources, + RunContext, + skill, +) + + +DEFAULT_BASE_URL = "https://api.hubapi.com" +OPERATIONS = json.loads("{\n \"delete_crm_v3_objects_contacts_contact_id_archive\": {\n \"base_url\": \"https://api.hubapi.com\",\n \"base_url_field\": \"OPENAPI_BASE_URL\",\n \"description\": \"Delete a contact by ID. Deleted contacts can be restored within 90 days of deletion. Learn more about the [data impacted by contact deletions](https://knowledge.hubspot.com/privacy-and-consent/understand-restorable-and-permanent-contact-deletions) and how to [restore archived records](https://knowledge.hubspot.com/records/restore-deleted-records).\",\n \"destructive\": true,\n \"method\": \"DELETE\",\n \"operation_id\": \"delete_crm_v3_objects_contacts_contact_id_archive\",\n \"parameters\": [\n {\n \"description\": \"\",\n \"in\": \"path\",\n \"name\": \"contactId\",\n \"required\": true,\n \"schema\": {\n \"example\": null,\n \"pattern\": \".+\",\n \"type\": \"string\"\n }\n }\n ],\n \"path\": \"/crm/v3/objects/contacts/{contactId}\",\n \"request_body\": null,\n \"security\": [\n {\n \"oauth2\": [\n \"crm.objects.contacts.write\"\n ]\n }\n ],\n \"skill_name\": \"delete_crm_v3_objects_contacts_contact_id_archive\",\n \"source_index\": 1,\n \"source_openapi_url\": \"https://raw.githubusercontent.com/HubSpot/HubSpot-public-api-spec-collection/main/PublicApiSpecs/CRM/Contacts/Rollouts/424/v3/contacts.json\",\n \"source_title\": \"Contacts\",\n \"summary\": \"Archive a contact\",\n \"tags\": [\n \"Basic\"\n ]\n },\n \"get_crm_v3_objects_contacts_contact_id_get_by_id\": {\n \"base_url\": \"https://api.hubapi.com\",\n \"base_url_field\": \"OPENAPI_BASE_URL\",\n \"description\": \"Retrieve a contact by its ID (`contactId`) or by a unique property (`idProperty`). You can specify what is returned using the `properties` query parameter.\",\n \"destructive\": false,\n \"method\": \"GET\",\n \"operation_id\": \"get_crm_v3_objects_contacts_contact_id_get_by_id\",\n \"parameters\": [\n {\n \"description\": \"\",\n \"in\": \"path\",\n \"name\": \"contactId\",\n \"required\": true,\n \"schema\": {\n \"example\": null,\n \"pattern\": \".+\",\n \"type\": \"string\"\n }\n },\n {\n \"description\": \"Whether to return only results that have been archived.\",\n \"in\": \"query\",\n \"name\": \"archived\",\n \"required\": false,\n \"schema\": {\n \"default\": false,\n \"example\": null,\n \"type\": \"boolean\"\n }\n },\n {\n \"description\": \"A comma separated list of object types to retrieve associated IDs for. If any of the specified associations do not exist, they will be ignored.\",\n \"in\": \"query\",\n \"name\": \"associations\",\n \"required\": false,\n \"schema\": {\n \"example\": null,\n \"items\": {\n \"example\": null,\n \"type\": \"string\"\n },\n \"type\": \"array\"\n }\n },\n {\n \"description\": \"The name of a property whose values are unique for this object type\",\n \"in\": \"query\",\n \"name\": \"idProperty\",\n \"required\": false,\n \"schema\": {\n \"example\": null,\n \"type\": \"string\"\n }\n },\n {\n \"description\": \"A comma separated list of the properties to be returned in the response. If any of the specified properties are not present on the requested object(s), they will be ignored.\",\n \"in\": \"query\",\n \"name\": \"properties\",\n \"required\": false,\n \"schema\": {\n \"example\": null,\n \"items\": {\n \"example\": null,\n \"type\": \"string\"\n },\n \"type\": \"array\"\n }\n },\n {\n \"description\": \"A comma separated list of the properties to be returned along with their history of previous values. If any of the specified properties are not present on the requested object(s), they will be ignored.\",\n \"in\": \"query\",\n \"name\": \"propertiesWithHistory\",\n \"required\": false,\n \"schema\": {\n \"example\": null,\n \"items\": {\n \"example\": null,\n \"type\": \"string\"\n },\n \"type\": \"array\"\n }\n }\n ],\n \"path\": \"/crm/v3/objects/contacts/{contactId}\",\n \"request_body\": null,\n \"security\": [\n {\n \"oauth2\": [\n \"crm.objects.contacts.read\"\n ]\n }\n ],\n \"skill_name\": \"get_crm_v3_objects_contacts_contact_id_get_by_id\",\n \"source_index\": 1,\n \"source_openapi_url\": \"https://raw.githubusercontent.com/HubSpot/HubSpot-public-api-spec-collection/main/PublicApiSpecs/CRM/Contacts/Rollouts/424/v3/contacts.json\",\n \"source_title\": \"Contacts\",\n \"summary\": \"Retrieve a contact\",\n \"tags\": [\n \"Basic\"\n ]\n },\n \"get_crm_v3_objects_contacts_get_page\": {\n \"base_url\": \"https://api.hubapi.com\",\n \"base_url_field\": \"OPENAPI_BASE_URL\",\n \"description\": \"Retrieve all contacts, using query parameters to specify the information that gets returned.\",\n \"destructive\": false,\n \"method\": \"GET\",\n \"operation_id\": \"get_crm_v3_objects_contacts_get_page\",\n \"parameters\": [\n {\n \"description\": \"The paging cursor token of the last successfully read resource will be returned as the `paging.next.after` JSON property of a paged response containing more results.\",\n \"in\": \"query\",\n \"name\": \"after\",\n \"required\": false,\n \"schema\": {\n \"example\": null,\n \"type\": \"string\"\n }\n },\n {\n \"description\": \"Whether to return only results that have been archived.\",\n \"in\": \"query\",\n \"name\": \"archived\",\n \"required\": false,\n \"schema\": {\n \"default\": false,\n \"example\": null,\n \"type\": \"boolean\"\n }\n },\n {\n \"description\": \"A comma separated list of object types to retrieve associated IDs for. If any of the specified associations do not exist, they will be ignored.\",\n \"in\": \"query\",\n \"name\": \"associations\",\n \"required\": false,\n \"schema\": {\n \"example\": null,\n \"items\": {\n \"example\": null,\n \"type\": \"string\"\n },\n \"type\": \"array\"\n }\n },\n {\n \"description\": \"The maximum number of results to display per page.\",\n \"in\": \"query\",\n \"name\": \"limit\",\n \"required\": false,\n \"schema\": {\n \"default\": 10,\n \"example\": null,\n \"format\": \"int32\",\n \"type\": \"integer\"\n }\n },\n {\n \"description\": \"A comma separated list of the properties to be returned in the response. If any of the specified properties are not present on the requested object(s), they will be ignored.\",\n \"in\": \"query\",\n \"name\": \"properties\",\n \"required\": false,\n \"schema\": {\n \"example\": null,\n \"items\": {\n \"example\": null,\n \"type\": \"string\"\n },\n \"type\": \"array\"\n }\n },\n {\n \"description\": \"A comma separated list of the properties to be returned along with their history of previous values. If any of the specified properties are not present on the requested object(s), they will be ignored. Usage of this parameter will reduce the maximum number of contacts that can be read by a single request.\",\n \"in\": \"query\",\n \"name\": \"propertiesWithHistory\",\n \"required\": false,\n \"schema\": {\n \"example\": null,\n \"items\": {\n \"example\": null,\n \"type\": \"string\"\n },\n \"type\": \"array\"\n }\n }\n ],\n \"path\": \"/crm/v3/objects/contacts\",\n \"request_body\": null,\n \"security\": [\n {\n \"oauth2\": [\n \"crm.objects.contacts.read\"\n ]\n }\n ],\n \"skill_name\": \"get_crm_v3_objects_contacts_get_page\",\n \"source_index\": 1,\n \"source_openapi_url\": \"https://raw.githubusercontent.com/HubSpot/HubSpot-public-api-spec-collection/main/PublicApiSpecs/CRM/Contacts/Rollouts/424/v3/contacts.json\",\n \"source_title\": \"Contacts\",\n \"summary\": \"Retrieve contacts\",\n \"tags\": [\n \"Basic\"\n ]\n },\n \"patch_crm_v3_objects_contacts_contact_id_update\": {\n \"base_url\": \"https://api.hubapi.com\",\n \"base_url_field\": \"OPENAPI_BASE_URL\",\n \"description\": \"Update an existing contact, identified by ID or email/unique property value. To identify a contact by ID, include the ID in the request URL path. To identify a contact by their email or other unique property, include the email/property value in the request URL path, and add the `idProperty` query parameter (`/crm/v3/objects/contacts/jon@website.com?idProperty=email`). Provided property values will be overwritten. Read-only and non-existent properties will result in an error. Properties values can be cleared by passing an empty string.\",\n \"destructive\": true,\n \"method\": \"PATCH\",\n \"operation_id\": \"patch_crm_v3_objects_contacts_contact_id_update\",\n \"parameters\": [\n {\n \"description\": \"\",\n \"in\": \"path\",\n \"name\": \"contactId\",\n \"required\": true,\n \"schema\": {\n \"example\": null,\n \"pattern\": \".+\",\n \"type\": \"string\"\n }\n },\n {\n \"description\": \"The name of a property whose values are unique for this object type\",\n \"in\": \"query\",\n \"name\": \"idProperty\",\n \"required\": false,\n \"schema\": {\n \"example\": null,\n \"type\": \"string\"\n }\n }\n ],\n \"path\": \"/crm/v3/objects/contacts/{contactId}\",\n \"request_body\": {\n \"$ref\": \"#/components/schemas/SimplePublicObjectInput\"\n },\n \"security\": [\n {\n \"oauth2\": [\n \"crm.objects.contacts.write\"\n ]\n }\n ],\n \"skill_name\": \"patch_crm_v3_objects_contacts_contact_id_update\",\n \"source_index\": 1,\n \"source_openapi_url\": \"https://raw.githubusercontent.com/HubSpot/HubSpot-public-api-spec-collection/main/PublicApiSpecs/CRM/Contacts/Rollouts/424/v3/contacts.json\",\n \"source_title\": \"Contacts\",\n \"summary\": \"Update a contact\",\n \"tags\": [\n \"Basic\"\n ]\n },\n \"post_crm_v3_objects_contacts_batch_archive_archive\": {\n \"base_url\": \"https://api.hubapi.com\",\n \"base_url_field\": \"OPENAPI_BASE_URL\",\n \"description\": \"Archive a batch of contacts by ID. Archived contacts can be restored within 90 days of deletion. Learn more about the [data impacted by contact deletions](https://knowledge.hubspot.com/privacy-and-consent/understand-restorable-and-permanent-contact-deletions) and how to [restore archived records](https://knowledge.hubspot.com/records/restore-deleted-records).\",\n \"destructive\": true,\n \"method\": \"POST\",\n \"operation_id\": \"post_crm_v3_objects_contacts_batch_archive_archive\",\n \"parameters\": [],\n \"path\": \"/crm/v3/objects/contacts/batch/archive\",\n \"request_body\": {\n \"$ref\": \"#/components/schemas/BatchInputSimplePublicObjectId\"\n },\n \"security\": [\n {\n \"oauth2\": [\n \"crm.objects.contacts.write\"\n ]\n }\n ],\n \"skill_name\": \"post_crm_v3_objects_contacts_batch_archive_archive\",\n \"source_index\": 1,\n \"source_openapi_url\": \"https://raw.githubusercontent.com/HubSpot/HubSpot-public-api-spec-collection/main/PublicApiSpecs/CRM/Contacts/Rollouts/424/v3/contacts.json\",\n \"source_title\": \"Contacts\",\n \"summary\": \"Archive a batch of contacts\",\n \"tags\": [\n \"Batch\"\n ]\n },\n \"post_crm_v3_objects_contacts_batch_create_create\": {\n \"base_url\": \"https://api.hubapi.com\",\n \"base_url_field\": \"OPENAPI_BASE_URL\",\n \"description\": \"Create a batch of contacts. The `inputs` array can contain a `properties` object to define property values for each record, along with an `associations` array to define [associations](https://developers.hubspot.com/docs/guides/api/crm/associations/associations-v4) with other CRM records.\",\n \"destructive\": true,\n \"method\": \"POST\",\n \"operation_id\": \"post_crm_v3_objects_contacts_batch_create_create\",\n \"parameters\": [],\n \"path\": \"/crm/v3/objects/contacts/batch/create\",\n \"request_body\": {\n \"$ref\": \"#/components/schemas/BatchInputSimplePublicObjectBatchInputForCreate\"\n },\n \"security\": [\n {\n \"oauth2\": [\n \"crm.objects.contacts.write\"\n ]\n }\n ],\n \"skill_name\": \"post_crm_v3_objects_contacts_batch_create_create\",\n \"source_index\": 1,\n \"source_openapi_url\": \"https://raw.githubusercontent.com/HubSpot/HubSpot-public-api-spec-collection/main/PublicApiSpecs/CRM/Contacts/Rollouts/424/v3/contacts.json\",\n \"source_title\": \"Contacts\",\n \"summary\": \"Create a batch of contacts\",\n \"tags\": [\n \"Batch\"\n ]\n },\n \"post_crm_v3_objects_contacts_batch_read_read\": {\n \"base_url\": \"https://api.hubapi.com\",\n \"base_url_field\": \"OPENAPI_BASE_URL\",\n \"description\": \"Retrieve a batch of contacts by ID (`contactId`) or unique property value (`idProperty`). \",\n \"destructive\": true,\n \"method\": \"POST\",\n \"operation_id\": \"post_crm_v3_objects_contacts_batch_read_read\",\n \"parameters\": [\n {\n \"description\": \"Whether to return only results that have been archived.\",\n \"in\": \"query\",\n \"name\": \"archived\",\n \"required\": false,\n \"schema\": {\n \"default\": false,\n \"example\": null,\n \"type\": \"boolean\"\n }\n }\n ],\n \"path\": \"/crm/v3/objects/contacts/batch/read\",\n \"request_body\": {\n \"$ref\": \"#/components/schemas/BatchReadInputSimplePublicObjectId\"\n },\n \"security\": [\n {\n \"oauth2\": [\n \"crm.objects.contacts.read\"\n ]\n }\n ],\n \"skill_name\": \"post_crm_v3_objects_contacts_batch_read_read\",\n \"source_index\": 1,\n \"source_openapi_url\": \"https://raw.githubusercontent.com/HubSpot/HubSpot-public-api-spec-collection/main/PublicApiSpecs/CRM/Contacts/Rollouts/424/v3/contacts.json\",\n \"source_title\": \"Contacts\",\n \"summary\": \"Retrieve a batch of contacts\",\n \"tags\": [\n \"Batch\"\n ]\n },\n \"post_crm_v3_objects_contacts_batch_update_update\": {\n \"base_url\": \"https://api.hubapi.com\",\n \"base_url_field\": \"OPENAPI_BASE_URL\",\n \"description\": \"Update a batch of contacts by ID (`contactId`) or unique property value (`idProperty`). Provided property values will be overwritten. Read-only and non-existent properties will result in an error. Properties values can be cleared by passing an empty string.\",\n \"destructive\": true,\n \"method\": \"POST\",\n \"operation_id\": \"post_crm_v3_objects_contacts_batch_update_update\",\n \"parameters\": [],\n \"path\": \"/crm/v3/objects/contacts/batch/update\",\n \"request_body\": {\n \"$ref\": \"#/components/schemas/BatchInputSimplePublicObjectBatchInput\"\n },\n \"security\": [\n {\n \"oauth2\": [\n \"crm.objects.contacts.write\"\n ]\n }\n ],\n \"skill_name\": \"post_crm_v3_objects_contacts_batch_update_update\",\n \"source_index\": 1,\n \"source_openapi_url\": \"https://raw.githubusercontent.com/HubSpot/HubSpot-public-api-spec-collection/main/PublicApiSpecs/CRM/Contacts/Rollouts/424/v3/contacts.json\",\n \"source_title\": \"Contacts\",\n \"summary\": \"Update a batch of contacts\",\n \"tags\": [\n \"Batch\"\n ]\n },\n \"post_crm_v3_objects_contacts_batch_upsert_upsert\": {\n \"base_url\": \"https://api.hubapi.com\",\n \"base_url_field\": \"OPENAPI_BASE_URL\",\n \"description\": \"Upsert a batch of contacts. The `inputs` array can contain a `properties` object to define property values for each record.\",\n \"destructive\": true,\n \"method\": \"POST\",\n \"operation_id\": \"post_crm_v3_objects_contacts_batch_upsert_upsert\",\n \"parameters\": [],\n \"path\": \"/crm/v3/objects/contacts/batch/upsert\",\n \"request_body\": {\n \"$ref\": \"#/components/schemas/BatchInputSimplePublicObjectBatchInputUpsert\"\n },\n \"security\": [\n {\n \"oauth2\": [\n \"crm.objects.contacts.write\"\n ]\n }\n ],\n \"skill_name\": \"post_crm_v3_objects_contacts_batch_upsert_upsert\",\n \"source_index\": 1,\n \"source_openapi_url\": \"https://raw.githubusercontent.com/HubSpot/HubSpot-public-api-spec-collection/main/PublicApiSpecs/CRM/Contacts/Rollouts/424/v3/contacts.json\",\n \"source_title\": \"Contacts\",\n \"summary\": \"Create or update a batch of contacts\",\n \"tags\": [\n \"Batch\"\n ]\n },\n \"post_crm_v3_objects_contacts_create\": {\n \"base_url\": \"https://api.hubapi.com\",\n \"base_url_field\": \"OPENAPI_BASE_URL\",\n \"description\": \"Create a single contact. Include a `properties` object to define [property values](https://developers.hubspot.com/docs/guides/api/crm/properties) for the contact, along with an `associations` array to define [associations](https://developers.hubspot.com/docs/guides/api/crm/associations/associations-v4) with other CRM records.\",\n \"destructive\": true,\n \"method\": \"POST\",\n \"operation_id\": \"post_crm_v3_objects_contacts_create\",\n \"parameters\": [],\n \"path\": \"/crm/v3/objects/contacts\",\n \"request_body\": {\n \"$ref\": \"#/components/schemas/SimplePublicObjectInputForCreate\"\n },\n \"security\": [\n {\n \"oauth2\": [\n \"crm.objects.contacts.write\"\n ]\n }\n ],\n \"skill_name\": \"post_crm_v3_objects_contacts_create\",\n \"source_index\": 1,\n \"source_openapi_url\": \"https://raw.githubusercontent.com/HubSpot/HubSpot-public-api-spec-collection/main/PublicApiSpecs/CRM/Contacts/Rollouts/424/v3/contacts.json\",\n \"source_title\": \"Contacts\",\n \"summary\": \"Create a contact\",\n \"tags\": [\n \"Basic\"\n ]\n },\n \"post_crm_v3_objects_contacts_gdpr_delete_purge\": {\n \"base_url\": \"https://api.hubapi.com\",\n \"base_url_field\": \"OPENAPI_BASE_URL\",\n \"description\": \"Permanently delete a contact and all associated content to follow GDPR. Use optional property `idProperty` set to `email` to identify contact by email address. If email address is not found, the email address will be added to a blocklist and prevent it from being used in the future. Learn more about [permanently deleting contacts](https://knowledge.hubspot.com/privacy-and-consent/how-do-i-perform-a-gdpr-delete-in-hubspot).\",\n \"destructive\": true,\n \"method\": \"POST\",\n \"operation_id\": \"post_crm_v3_objects_contacts_gdpr_delete_purge\",\n \"parameters\": [],\n \"path\": \"/crm/v3/objects/contacts/gdpr-delete\",\n \"request_body\": {\n \"$ref\": \"#/components/schemas/PublicGdprDeleteInput\"\n },\n \"security\": [\n {\n \"oauth2\": [\n \"crm.objects.contacts.write\"\n ]\n }\n ],\n \"skill_name\": \"post_crm_v3_objects_contacts_gdpr_delete_purge\",\n \"source_index\": 1,\n \"source_openapi_url\": \"https://raw.githubusercontent.com/HubSpot/HubSpot-public-api-spec-collection/main/PublicApiSpecs/CRM/Contacts/Rollouts/424/v3/contacts.json\",\n \"source_title\": \"Contacts\",\n \"summary\": \"Permanently delete a contact (GDPR-compliant)\",\n \"tags\": [\n \"Advanced\"\n ]\n },\n \"post_crm_v3_objects_contacts_merge_merge\": {\n \"base_url\": \"https://api.hubapi.com\",\n \"base_url_field\": \"OPENAPI_BASE_URL\",\n \"description\": \"Merge two contact records. Learn more about [merging records](https://knowledge.hubspot.com/records/merge-records). \",\n \"destructive\": true,\n \"method\": \"POST\",\n \"operation_id\": \"post_crm_v3_objects_contacts_merge_merge\",\n \"parameters\": [],\n \"path\": \"/crm/v3/objects/contacts/merge\",\n \"request_body\": {\n \"$ref\": \"#/components/schemas/PublicMergeInput\"\n },\n \"security\": [\n {\n \"oauth2\": [\n \"crm.objects.contacts.write\"\n ]\n }\n ],\n \"skill_name\": \"post_crm_v3_objects_contacts_merge_merge\",\n \"source_index\": 1,\n \"source_openapi_url\": \"https://raw.githubusercontent.com/HubSpot/HubSpot-public-api-spec-collection/main/PublicApiSpecs/CRM/Contacts/Rollouts/424/v3/contacts.json\",\n \"source_title\": \"Contacts\",\n \"summary\": \"Merge two contacts\",\n \"tags\": [\n \"Advanced\"\n ]\n },\n \"post_crm_v3_objects_contacts_search_do_search\": {\n \"base_url\": \"https://api.hubapi.com\",\n \"base_url_field\": \"OPENAPI_BASE_URL\",\n \"description\": \"Search for contacts by filtering on properties, searching through associations, and sorting results. Learn more about [CRM search](https://developers.hubspot.com/docs/guides/api/crm/search#make-a-search-request).\",\n \"destructive\": true,\n \"method\": \"POST\",\n \"operation_id\": \"post_crm_v3_objects_contacts_search_do_search\",\n \"parameters\": [],\n \"path\": \"/crm/v3/objects/contacts/search\",\n \"request_body\": {\n \"$ref\": \"#/components/schemas/PublicObjectSearchRequest\"\n },\n \"security\": [\n {\n \"oauth2\": [\n \"crm.objects.contacts.read\"\n ]\n }\n ],\n \"skill_name\": \"post_crm_v3_objects_contacts_search_do_search\",\n \"source_index\": 1,\n \"source_openapi_url\": \"https://raw.githubusercontent.com/HubSpot/HubSpot-public-api-spec-collection/main/PublicApiSpecs/CRM/Contacts/Rollouts/424/v3/contacts.json\",\n \"source_title\": \"Contacts\",\n \"summary\": \"Search for contacts\",\n \"tags\": [\n \"Search\"\n ]\n }\n}") +OPERATION_GROUPS = json.loads("[]") +ROOT_SECURITY = json.loads("[]") +SECURITY_SCHEMES = json.loads("{\n \"developer_hapikey\": {\n \"in\": \"query\",\n \"name\": \"hapikey\",\n \"type\": \"apiKey\"\n },\n \"oauth2\": {\n \"flows\": {\n \"authorizationCode\": {\n \"authorizationUrl\": \"https://app.hubspot.com/oauth/authorize\",\n \"scopes\": {\n \"crm.objects.commercepayments.read\": \"\",\n \"crm.objects.contacts.read\": \"\",\n \"crm.objects.contacts.write\": \"\",\n \"oauth\": \"\"\n },\n \"tokenUrl\": \"https://api.hubapi.com/oauth/v1/token\"\n }\n },\n \"type\": \"oauth2\"\n },\n \"private_apps\": {\n \"in\": \"header\",\n \"name\": \"private-app\",\n \"type\": \"apiKey\"\n },\n \"private_apps_legacy\": {\n \"in\": \"header\",\n \"name\": \"private-app-legacy\",\n \"type\": \"apiKey\"\n }\n}") +SECURITY_FIELDS = json.loads("{\n \"developer_hapikey\": {\n \"field\": \"HAPIKEY\",\n \"kind\": \"apiKey\",\n \"location\": \"query\",\n \"name\": \"hapikey\"\n },\n \"oauth2\": {\n \"field\": \"OAUTH2\",\n \"kind\": \"oauth2\",\n \"scheme\": \"Bearer\"\n },\n \"private_apps\": {\n \"field\": \"PRIVATE_APP\",\n \"kind\": \"apiKey\",\n \"location\": \"header\",\n \"name\": \"private-app\"\n },\n \"private_apps_legacy\": {\n \"field\": \"PRIVATE_APP_LEGACY\",\n \"kind\": \"apiKey\",\n \"location\": \"header\",\n \"name\": \"private-app-legacy\"\n }\n}") +PATH_PARAMETER_RE = re.compile(r"{([^}/]+)}") +SOURCE_ROOT = Path(globals().get("__file__", "agent.py")).resolve().parent +SOURCE_SKILLS_DIR = SOURCE_ROOT / "skills" +RUNTIME_SKILLS_DIR = ".deepagents/openapi-skills/" + + +class OperationInput(BaseModel): + parameters: dict[str, Any] = Field( + default_factory=dict, + description="Path, query, header, and cookie parameters keyed by OpenAPI parameter name.", + ) + body: Any | None = Field(default=None, description="JSON request body, when the operation accepts one.") + + +class HubspotContactsOpenapiAgent(A2AAgent): + name = "hubspot-contacts-openapi-agent" + description = "CRM object API for creating, reading, searching, and batching HubSpot contacts." + version = "v3" + consumer_setup = ConsumerSetup.from_fields( + ConsumerSetupField.config("OPENAPI_BASE_URL", label="API base URL", description="Override the default API server (https://api.hubapi.com).", required=False, input_type="url"), + ConsumerSetupField.secret("HAPIKEY", label="developer_hapikey API key", description="Sent as query parameter 'hapikey'.", required=False), + ConsumerSetupField.secret("OAUTH2", label="oauth2 access token", description="OAuth/OIDC access token.", required=True), + ConsumerSetupField.secret("PRIVATE_APP", label="private_apps API key", description="Sent as header parameter 'private-app'.", required=False), + ConsumerSetupField.secret("PRIVATE_APP_LEGACY", label="private_apps_legacy API key", description="Sent as header parameter 'private-app-legacy'.", required=False), + ) + llm_provisioning = LLMProvisioning.PLATFORM + pricing = Pricing( + price_per_call_usd=0.0, + caller_pays_llm=True, + notes="Uses the caller's saved LLM credential through ctx.llm.", + ) + resources = Resources(cpu="200m", memory="512Mi") + egress = EgressPolicy( + allow_hosts=('api.hubapi.com',), + deny_internet_by_default=True, + ) + tools_used = ("openapi", "deepagents") + capabilities = { + "openapi_auto_agent": { + "operation_count": len(OPERATIONS), + "default_base_url": DEFAULT_BASE_URL, + "source_openapi_url": 'https://raw.githubusercontent.com/HubSpot/HubSpot-public-api-spec-collection/main/PublicApiSpecs/CRM/Contacts/Rollouts/424/v3/contacts.json', + "source_openapi_urls": ['https://raw.githubusercontent.com/HubSpot/HubSpot-public-api-spec-collection/main/PublicApiSpecs/CRM/Contacts/Rollouts/424/v3/contacts.json'], + "server_urls": ['https://api.hubapi.com'], + "regenerable": True, + "security_schemes": list(SECURITY_SCHEMES), + } + } + + @skill( + name="auto", + description="Use the OpenAPI service to complete a natural-language goal.", + tags=("openapi", "auto"), + timeout_seconds=900, + ) + async def auto(self, ctx: RunContext, goal: str) -> dict[str, Any]: + creds = ctx.llm + if not creds.api_key: + return { + "error": "llm_credentials_missing", + "final": ( + "LLM key required. Add an LLM credential in Settings > " + "LLM credentials before running this agent." + ), + "messages": [], + } + graph = create_a2a_deep_agent( + ctx, + creds=creds, + tools=self._operation_tools(ctx), + system_prompt=self._system_prompt(), + ) + result = await graph.ainvoke({"messages": [{"role": "user", "content": goal}]}) + messages = result.get("messages", []) if isinstance(result, dict) else [] + final = _message_text(messages[-1]) if messages else result + return { + "final": final, + "messages": [_message_to_dict(message) for message in messages[-8:]], + } + + @skill( + name="get_crm_v3_objects_contacts_get_page", + description="GET /crm/v3/objects/contacts - Retrieve contacts", + tags=('Basic',), + timeout_seconds=60, + ) + async def get_crm_v3_objects_contacts_get_page( + self, + ctx: RunContext, + parameters: dict[str, Any] | None = None, + body: Any | None = None, + ) -> dict[str, Any]: + return await self._request( + ctx, + "get_crm_v3_objects_contacts_get_page", + parameters=parameters or {}, + body=body, + ) + + @skill( + name="post_crm_v3_objects_contacts_create", + description="POST /crm/v3/objects/contacts - Create a contact", + tags=('Basic',), + timeout_seconds=120, + ) + async def post_crm_v3_objects_contacts_create( + self, + ctx: RunContext, + parameters: dict[str, Any] | None = None, + body: Any | None = None, + ) -> dict[str, Any]: + return await self._request( + ctx, + "post_crm_v3_objects_contacts_create", + parameters=parameters or {}, + body=body, + ) + + @skill( + name="post_crm_v3_objects_contacts_batch_archive_archive", + description="POST /crm/v3/objects/contacts/batch/archive - Archive a batch of contacts", + tags=('Batch',), + timeout_seconds=120, + ) + async def post_crm_v3_objects_contacts_batch_archive_archive( + self, + ctx: RunContext, + parameters: dict[str, Any] | None = None, + body: Any | None = None, + ) -> dict[str, Any]: + return await self._request( + ctx, + "post_crm_v3_objects_contacts_batch_archive_archive", + parameters=parameters or {}, + body=body, + ) + + @skill( + name="post_crm_v3_objects_contacts_batch_create_create", + description="POST /crm/v3/objects/contacts/batch/create - Create a batch of contacts", + tags=('Batch',), + timeout_seconds=120, + ) + async def post_crm_v3_objects_contacts_batch_create_create( + self, + ctx: RunContext, + parameters: dict[str, Any] | None = None, + body: Any | None = None, + ) -> dict[str, Any]: + return await self._request( + ctx, + "post_crm_v3_objects_contacts_batch_create_create", + parameters=parameters or {}, + body=body, + ) + + @skill( + name="post_crm_v3_objects_contacts_batch_read_read", + description="POST /crm/v3/objects/contacts/batch/read - Retrieve a batch of contacts", + tags=('Batch',), + timeout_seconds=120, + ) + async def post_crm_v3_objects_contacts_batch_read_read( + self, + ctx: RunContext, + parameters: dict[str, Any] | None = None, + body: Any | None = None, + ) -> dict[str, Any]: + return await self._request( + ctx, + "post_crm_v3_objects_contacts_batch_read_read", + parameters=parameters or {}, + body=body, + ) + + @skill( + name="post_crm_v3_objects_contacts_batch_update_update", + description="POST /crm/v3/objects/contacts/batch/update - Update a batch of contacts", + tags=('Batch',), + timeout_seconds=120, + ) + async def post_crm_v3_objects_contacts_batch_update_update( + self, + ctx: RunContext, + parameters: dict[str, Any] | None = None, + body: Any | None = None, + ) -> dict[str, Any]: + return await self._request( + ctx, + "post_crm_v3_objects_contacts_batch_update_update", + parameters=parameters or {}, + body=body, + ) + + @skill( + name="post_crm_v3_objects_contacts_batch_upsert_upsert", + description="POST /crm/v3/objects/contacts/batch/upsert - Create or update a batch of contacts", + tags=('Batch',), + timeout_seconds=120, + ) + async def post_crm_v3_objects_contacts_batch_upsert_upsert( + self, + ctx: RunContext, + parameters: dict[str, Any] | None = None, + body: Any | None = None, + ) -> dict[str, Any]: + return await self._request( + ctx, + "post_crm_v3_objects_contacts_batch_upsert_upsert", + parameters=parameters or {}, + body=body, + ) + + @skill( + name="post_crm_v3_objects_contacts_gdpr_delete_purge", + description="POST /crm/v3/objects/contacts/gdpr-delete - Permanently delete a contact (GDPR-compliant)", + tags=('Advanced',), + timeout_seconds=120, + ) + async def post_crm_v3_objects_contacts_gdpr_delete_purge( + self, + ctx: RunContext, + parameters: dict[str, Any] | None = None, + body: Any | None = None, + ) -> dict[str, Any]: + return await self._request( + ctx, + "post_crm_v3_objects_contacts_gdpr_delete_purge", + parameters=parameters or {}, + body=body, + ) + + @skill( + name="post_crm_v3_objects_contacts_merge_merge", + description="POST /crm/v3/objects/contacts/merge - Merge two contacts", + tags=('Advanced',), + timeout_seconds=120, + ) + async def post_crm_v3_objects_contacts_merge_merge( + self, + ctx: RunContext, + parameters: dict[str, Any] | None = None, + body: Any | None = None, + ) -> dict[str, Any]: + return await self._request( + ctx, + "post_crm_v3_objects_contacts_merge_merge", + parameters=parameters or {}, + body=body, + ) + + @skill( + name="post_crm_v3_objects_contacts_search_do_search", + description="POST /crm/v3/objects/contacts/search - Search for contacts", + tags=('Search',), + timeout_seconds=120, + ) + async def post_crm_v3_objects_contacts_search_do_search( + self, + ctx: RunContext, + parameters: dict[str, Any] | None = None, + body: Any | None = None, + ) -> dict[str, Any]: + return await self._request( + ctx, + "post_crm_v3_objects_contacts_search_do_search", + parameters=parameters or {}, + body=body, + ) + + @skill( + name="get_crm_v3_objects_contacts_contact_id_get_by_id", + description="GET /crm/v3/objects/contacts/{contactId} - Retrieve a contact", + tags=('Basic',), + timeout_seconds=60, + ) + async def get_crm_v3_objects_contacts_contact_id_get_by_id( + self, + ctx: RunContext, + parameters: dict[str, Any] | None = None, + body: Any | None = None, + ) -> dict[str, Any]: + return await self._request( + ctx, + "get_crm_v3_objects_contacts_contact_id_get_by_id", + parameters=parameters or {}, + body=body, + ) + + @skill( + name="delete_crm_v3_objects_contacts_contact_id_archive", + description="DELETE /crm/v3/objects/contacts/{contactId} - Archive a contact", + tags=('Basic',), + timeout_seconds=120, + ) + async def delete_crm_v3_objects_contacts_contact_id_archive( + self, + ctx: RunContext, + parameters: dict[str, Any] | None = None, + body: Any | None = None, + ) -> dict[str, Any]: + return await self._request( + ctx, + "delete_crm_v3_objects_contacts_contact_id_archive", + parameters=parameters or {}, + body=body, + ) + + @skill( + name="patch_crm_v3_objects_contacts_contact_id_update", + description="PATCH /crm/v3/objects/contacts/{contactId} - Update a contact", + tags=('Basic',), + timeout_seconds=120, + ) + async def patch_crm_v3_objects_contacts_contact_id_update( + self, + ctx: RunContext, + parameters: dict[str, Any] | None = None, + body: Any | None = None, + ) -> dict[str, Any]: + return await self._request( + ctx, + "patch_crm_v3_objects_contacts_contact_id_update", + parameters=parameters or {}, + body=body, + ) + + def _operation_subagents(self, ctx: RunContext, skills_root: str) -> list[dict[str, Any]]: + subagents: list[dict[str, Any]] = [] + for group in OPERATION_GROUPS: + skill_path = ( + f"{skills_root}{group['skill_dir']}/" + if skills_root + else "" + ) + subagent = { + "name": group["subagent_name"], + "description": group["description"], + "system_prompt": self._subagent_prompt(group), + "tools": self._operation_tools(ctx, set(group["operation_ids"])), + } + if skill_path: + subagent["skills"] = [skill_path] + subagents.append(subagent) + return subagents + + def _operation_tools( + self, + ctx: RunContext, + operation_ids: set[str] | None = None, + ) -> list[StructuredTool]: + tools: list[StructuredTool] = [] + for operation_id, operation in OPERATIONS.items(): + if operation_ids is not None and operation_id not in operation_ids: + continue + async def call( + parameters: dict[str, Any] | None = None, + body: Any | None = None, + *, + _operation_id: str = operation_id, + ) -> dict[str, Any]: + return await self._request( + ctx, + _operation_id, + parameters=parameters or {}, + body=body, + ) + + tools.append( + StructuredTool.from_function( + coroutine=call, + name=operation["skill_name"], + description=self._tool_description(operation), + args_schema=OperationInput, + ) + ) + return tools + + def _system_prompt(self) -> str: + if OPERATION_GROUPS: + lines = [ + "You coordinate route-specific OpenAPI subagents.", + "Delegate API work to the matching subagent with the task tool.", + "Do not invent API responses; ask subagents to call their tools for real results.", + "If a subagent reports missing consumer setup, tell the user which setup field is required.", + "If a subagent reports that the generated OpenAPI agent may be stale, ask whether the user wants to refresh it from the latest OpenAPI spec.", + "", + "Available route groups:", + ] + for group in OPERATION_GROUPS: + lines.append( + f"- {group['subagent_name']}: {group['description']}" + ) + return "\n".join(lines) + lines = [ + "You operate an API through generated OpenAPI tools.", + "Call tools to get real results. Do not invent API responses.", + "For write, update, or delete operations, explain the intended action before calling the tool.", + "If an operation reports missing consumer setup, tell the user which setup field is required.", + "If an operation returns 404, 405, 410, or a schema/validation error that suggests the live API no longer matches these tools, tell the user this generated agent may need to be refreshed from the latest OpenAPI spec and ask whether they want to refresh it.", + "", + "Available operations:", + ] + for operation in OPERATIONS.values(): + marker = "WRITE" if operation.get("destructive") else "READ" + lines.append( + f"- {operation['skill_name']}: {marker} {operation['method']} {operation['path']} — {operation['summary']}" + ) + return "\n".join(lines) + + def _subagent_prompt(self, group: dict[str, Any]) -> str: + lines = [ + "You operate one route group from a generated OpenAPI API.", + "Use the available operation tools to make real API calls. Do not invent API responses.", + "Read the route group's SKILL.md when it applies; it lists every route you cover.", + "For write, update, or delete operations, explain the intended action before calling the tool.", + "If an operation reports missing consumer setup, return the exact setup field name.", + "If an operation returns 404, 405, 410, or a schema/validation error that suggests the live API no longer matches these tools, say the generated agent may need to be refreshed from the latest OpenAPI spec.", + "", + f"Route group: {group['label']}", + f"Common prefix: {group.get('common_prefix') or '/'}", + "Available operation tools:", + ] + for operation_id in group["operation_ids"]: + operation = OPERATIONS[operation_id] + marker = "WRITE" if operation.get("destructive") else "READ" + lines.append( + f"- {operation['skill_name']}: {marker} {operation['method']} {operation['path']} - {operation['summary']}" + ) + return "\n".join(lines) + + def _tool_description(self, operation: dict[str, Any]) -> str: + parts = [ + f"{operation['method']} {operation['path']}", + str(operation.get("description") or operation.get("summary") or ""), + ] + if operation.get("parameters"): + parts.append( + "Parameters: " + + ", ".join( + f"{p['name']} in {p['in']}{' required' if p.get('required') else ''}" + for p in operation["parameters"] + ) + ) + if operation.get("request_body") is not None: + parts.append("Accepts a JSON request body.") + return "\n".join(part for part in parts if part) + + async def _request( + self, + ctx: RunContext, + operation_id: str, + *, + parameters: dict[str, Any], + body: Any | None, + ) -> dict[str, Any]: + operation = OPERATIONS[operation_id] + default_base_url = str(operation.get("base_url") or DEFAULT_BASE_URL).rstrip("/") + base_url_field = str(operation.get("base_url_field") or "OPENAPI_BASE_URL") + base_url = str(ctx.consumer_config(base_url_field, default_base_url) or default_base_url).rstrip("/") + url, query, headers = self._request_parts(ctx, operation, parameters) + request_kwargs: dict[str, Any] = { + "params": query, + "headers": headers, + } + if body is not None: + if isinstance(body, (bytes, bytearray)): + body = body.decode("utf-8", errors="replace") + if isinstance(body, str): + stripped = body.strip() + if stripped and stripped[0] in "{[": + try: + body = json.loads(stripped) + except ValueError: + pass + request_kwargs["json"] = body + async with httpx.AsyncClient(timeout=60.0, follow_redirects=True) as client: + response = await client.request( + operation["method"], + f"{base_url}{url}", + **request_kwargs, + ) + content_type = response.headers.get("content-type", "") + try: + payload: Any = response.json() if "json" in content_type else response.text + except ValueError: + payload = response.text + if response.status_code >= 400: + out = { + "ok": False, + "status_code": response.status_code, + "operation_id": operation_id, + "error": payload, + } + if response.status_code in {404, 405, 410, 422}: + out["stale_openapi_hint"] = ( + "This generated OpenAPI agent may be stale. Ask the user " + "whether they want to refresh it from the latest OpenAPI spec." + ) + return out + return { + "ok": True, + "status_code": response.status_code, + "operation_id": operation_id, + "result": payload, + } + + def _request_parts( + self, + ctx: RunContext, + operation: dict[str, Any], + parameters: dict[str, Any], + ) -> tuple[str, dict[str, Any], dict[str, str]]: + path = operation["path"] + query: dict[str, Any] = {} + headers: dict[str, str] = {} + cookies: dict[str, Any] = {} + for param in operation.get("parameters") or []: + name = param["name"] + if name not in parameters: + if param.get("required"): + raise ValueError(f"missing required parameter {name!r}") + continue + value = parameters[name] + location = param["in"] + if location == "path": + path = path.replace("{" + name + "}", str(value)) + elif location == "query": + query[name] = value + elif location == "header": + headers[name] = str(value) + elif location == "cookie": + cookies[name] = value + unresolved = PATH_PARAMETER_RE.findall(path) + if unresolved: + missing = ", ".join(sorted(set(unresolved))) + raise ValueError("missing required path parameter(s): " + missing) + if cookies: + headers["cookie"] = "; ".join(f"{key}={value}" for key, value in cookies.items()) + auth_headers, auth_query = self._auth_for_operation(ctx, operation) + headers.update(auth_headers) + query.update(auth_query) + return path, query, headers + + def _auth_for_operation(self, ctx: RunContext, operation: dict[str, Any]) -> tuple[dict[str, str], dict[str, Any]]: + requirements = operation.get("security") + if requirements is None: + requirements = ROOT_SECURITY + if requirements == []: + return {}, {} + missing: list[str] = [] + for requirement in requirements or []: + if not isinstance(requirement, dict) or not requirement: + return {}, {} + headers: dict[str, str] = {} + query: dict[str, Any] = {} + ok = True + for scheme_name in requirement: + mapping = SECURITY_FIELDS.get(scheme_name) or {} + secret_name = mapping.get("field") + value = _consumer_secret_optional(ctx, secret_name) if secret_name else "" + if not value: + ok = False + if secret_name: + missing.append(secret_name) + continue + kind = mapping.get("kind") + if kind == "apiKey": + prefix = mapping.get("prefix") or "" + sent_value = f"{prefix}{value}" + if mapping.get("location") == "query": + query[mapping.get("name") or scheme_name] = sent_value + else: + headers[mapping.get("name") or scheme_name] = sent_value + elif kind in {"http", "oauth2"}: + scheme = mapping.get("scheme") or "Bearer" + if scheme.lower() == "basic": + token = base64.b64encode(value.encode("utf-8")).decode("ascii") + headers["authorization"] = f"Basic {token}" + else: + headers["authorization"] = f"{scheme} {value}" + else: + ok = False + if ok: + return headers, query + if missing: + raise ConsumerSetupMissing( + "operation requires consumer setup secret(s): " + + ", ".join(sorted(set(missing))) + ) + return {}, {} + + +def _consumer_secret_optional(ctx: RunContext, name: str | None) -> str: + if not name: + return "" + try: + return ctx.consumer_secret(name) + except ConsumerSetupMissing: + return "" + + +def _runtime_skills_root(ctx: RunContext) -> str: + workspace = getattr(ctx, "_workspace", None) + prefixes = tuple(getattr(workspace, "write_prefixes", ()) or ()) + if not prefixes: + outputs_prefix = getattr(workspace, "outputs_prefix", None) + prefixes = (outputs_prefix or "outputs/",) + prefix = str(prefixes[0]).strip("/") + return f"/{prefix}/{RUNTIME_SKILLS_DIR}" if prefix else f"/{RUNTIME_SKILLS_DIR}" + + +def _seed_runtime_skills(backend: Any, ctx: RunContext) -> str: + if not SOURCE_SKILLS_DIR.exists(): + return "" + runtime_root = _runtime_skills_root(ctx) + uploads: list[tuple[str, bytes]] = [] + for path in SOURCE_SKILLS_DIR.rglob("*"): + if not path.is_file(): + continue + rel = path.relative_to(SOURCE_SKILLS_DIR).as_posix() + uploads.append((runtime_root + rel, path.read_bytes())) + if not uploads: + return "" + backend.upload_files(uploads) + return runtime_root + + +def _message_text(message: Any) -> Any: + content = getattr(message, "content", message) + return content + + +def _message_to_dict(message: Any) -> dict[str, Any]: + if isinstance(message, BaseMessage): + return message.model_dump(mode="json") + if hasattr(message, "model_dump"): + return message.model_dump(mode="json") + if isinstance(message, dict): + return message + return {"content": str(message)} + + +agent = HubspotContactsOpenapiAgent() diff --git a/openapi.json b/openapi.json new file mode 100644 index 0000000..2a266ab --- /dev/null +++ b/openapi.json @@ -0,0 +1,2354 @@ +{ + "components": { + "responses": { + "Error": { + "content": { + "*/*": { + "example": null, + "schema": { + "$ref": "#/components/schemas/Error" + } + } + }, + "description": "An error occurred." + } + }, + "schemas": { + "AssociatedId": { + "description": "Contains the id and type of an association", + "example": null, + "properties": { + "id": { + "description": "The ID for the association type.", + "example": null, + "type": "string" + }, + "type": { + "description": "The type of associations.", + "example": "deal_to_contact", + "type": "string" + } + }, + "required": [ + "id", + "type" + ], + "type": "object" + }, + "AssociationSpec": { + "description": "Defines the type, direction, and details of the relationship between two CRM objects.", + "example": null, + "properties": { + "associationCategory": { + "description": "The category of the association, such as \"HUBSPOT_DEFINED\".", + "enum": [ + "HUBSPOT_DEFINED", + "INTEGRATOR_DEFINED", + "USER_DEFINED", + "WORK" + ], + "example": null, + "type": "string" + }, + "associationTypeId": { + "description": "The ID representing the specific type of association.", + "example": null, + "format": "int32", + "type": "integer" + } + }, + "required": [ + "associationCategory", + "associationTypeId" + ], + "type": "object" + }, + "BatchInputSimplePublicObjectBatchInput": { + "example": null, + "properties": { + "inputs": { + "example": null, + "items": { + "$ref": "#/components/schemas/SimplePublicObjectBatchInput" + }, + "type": "array" + } + }, + "required": [ + "inputs" + ], + "type": "object" + }, + "BatchInputSimplePublicObjectBatchInputForCreate": { + "example": null, + "properties": { + "inputs": { + "example": null, + "items": { + "$ref": "#/components/schemas/SimplePublicObjectBatchInputForCreate" + }, + "type": "array" + } + }, + "required": [ + "inputs" + ], + "type": "object" + }, + "BatchInputSimplePublicObjectBatchInputUpsert": { + "example": null, + "properties": { + "inputs": { + "example": null, + "items": { + "$ref": "#/components/schemas/SimplePublicObjectBatchInputUpsert" + }, + "type": "array" + } + }, + "required": [ + "inputs" + ], + "type": "object" + }, + "BatchInputSimplePublicObjectId": { + "example": null, + "properties": { + "inputs": { + "example": null, + "items": { + "$ref": "#/components/schemas/SimplePublicObjectId" + }, + "type": "array" + } + }, + "required": [ + "inputs" + ], + "type": "object" + }, + "BatchReadInputSimplePublicObjectId": { + "description": "Specifies the input for reading a batch of CRM objects, including arrays of object IDs, requested property names (with optional history), and an optional unique identifying property.", + "example": null, + "properties": { + "idProperty": { + "description": "When using a custom unique value property to retrieve records, the name of the property. Do not include this parameter if retrieving by record ID.", + "example": null, + "type": "string" + }, + "inputs": { + "example": null, + "items": { + "$ref": "#/components/schemas/SimplePublicObjectId" + }, + "type": "array" + }, + "properties": { + "description": "Key-value pairs for setting properties for the new object.", + "example": null, + "items": { + "example": null, + "type": "string" + }, + "type": "array" + }, + "propertiesWithHistory": { + "description": "Key-value pairs for setting properties for the new object and their histories.", + "example": null, + "items": { + "example": null, + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "inputs", + "properties", + "propertiesWithHistory" + ], + "type": "object" + }, + "BatchResponseSimplePublicObject": { + "description": "A public object batch response object", + "example": null, + "properties": { + "completedAt": { + "description": "The timestamp when the batch processing was completed, in ISO 8601 format.", + "example": null, + "format": "date-time", + "type": "string" + }, + "links": { + "additionalProperties": { + "example": null, + "type": "string" + }, + "description": "An object containing relevant links related to the batch request.", + "example": null, + "type": "object" + }, + "requestedAt": { + "description": "The timestamp when the batch request was initially made, in ISO 8601 format.", + "example": null, + "format": "date-time", + "type": "string" + }, + "results": { + "example": null, + "items": { + "$ref": "#/components/schemas/SimplePublicObject" + }, + "type": "array" + }, + "startedAt": { + "description": "The timestamp when the batch processing began, in ISO 8601 format.", + "example": null, + "format": "date-time", + "type": "string" + }, + "status": { + "description": "The status of the batch processing request. The expected value is \"COMPLETE\".", + "enum": [ + "CANCELED", + "COMPLETE", + "PENDING", + "PROCESSING" + ], + "example": null, + "type": "string" + } + }, + "required": [ + "completedAt", + "results", + "startedAt", + "status" + ], + "type": "object" + }, + "BatchResponseSimplePublicObjectWithErrors": { + "description": "Represents the result of a batch operation on CRM objects, including the processing status, batch results, timestamps, and a list of any errors encountered during the operation.", + "example": null, + "properties": { + "completedAt": { + "description": "The timestamp when the batch process was completed, in ISO 8601 format.", + "example": null, + "format": "date-time", + "type": "string" + }, + "errors": { + "example": null, + "items": { + "$ref": "#/components/schemas/StandardError" + }, + "type": "array" + }, + "links": { + "additionalProperties": { + "example": null, + "type": "string" + }, + "description": "An object containing relevant links related to the batch request.", + "example": null, + "type": "object" + }, + "numErrors": { + "description": "The number of errors encountered during the batch process.", + "example": null, + "format": "int32", + "type": "integer" + }, + "requestedAt": { + "description": "The timestamp when the batch process was initiated, in ISO 8601 format.", + "example": null, + "format": "date-time", + "type": "string" + }, + "results": { + "example": null, + "items": { + "$ref": "#/components/schemas/SimplePublicObject" + }, + "type": "array" + }, + "startedAt": { + "description": "The timestamp when the batch process began execution, in ISO 8601 format.", + "example": null, + "format": "date-time", + "type": "string" + }, + "status": { + "description": "The status of the batch processing request - \"PENDING\", \"PROCESSING\", \"CANCELLED\", or \"COMPLETE\"", + "enum": [ + "CANCELED", + "COMPLETE", + "PENDING", + "PROCESSING" + ], + "example": null, + "type": "string" + } + }, + "required": [ + "completedAt", + "results", + "startedAt", + "status" + ], + "type": "object" + }, + "BatchResponseSimplePublicUpsertObject": { + "description": "Represents the result of a batch upsert operation, including the operation\u2019s status, timestamps, and a list of successfully created or updated objects.", + "example": null, + "properties": { + "completedAt": { + "description": "The timestamp when the batch process was completed, in ISO 8601 format.", + "example": null, + "format": "date-time", + "type": "string" + }, + "links": { + "additionalProperties": { + "example": null, + "type": "string" + }, + "description": "An object containing relevant links related to the batch request.", + "example": null, + "type": "object" + }, + "requestedAt": { + "description": "The timestamp when the batch process was initiated, in ISO 8601 format.", + "example": null, + "format": "date-time", + "type": "string" + }, + "results": { + "example": null, + "items": { + "$ref": "#/components/schemas/SimplePublicUpsertObject" + }, + "type": "array" + }, + "startedAt": { + "description": "The timestamp when the batch process began execution, in ISO 8601 format.", + "example": null, + "format": "date-time", + "type": "string" + }, + "status": { + "description": "The status of the batch processing request. Can be: \"PENDING\", \"PROCESSING\", \"CANCELED\", or \"COMPLETE\".", + "enum": [ + "CANCELED", + "COMPLETE", + "PENDING", + "PROCESSING" + ], + "example": null, + "type": "string" + } + }, + "required": [ + "completedAt", + "results", + "startedAt", + "status" + ], + "type": "object" + }, + "BatchResponseSimplePublicUpsertObjectWithErrors": { + "description": "Represents the response from a batch upsert operation, including the status, timestamps, successfully processed objects, and any errors that occurred during processing.", + "example": null, + "properties": { + "completedAt": { + "description": "The timestamp when the batch process was completed, in ISO 8601 format.", + "example": null, + "format": "date-time", + "type": "string" + }, + "errors": { + "example": null, + "items": { + "$ref": "#/components/schemas/StandardError" + }, + "type": "array" + }, + "links": { + "additionalProperties": { + "example": null, + "type": "string" + }, + "description": "An object containing relevant links related to the batch request.", + "example": null, + "type": "object" + }, + "numErrors": { + "description": "The number of errors encountered during the batch process.", + "example": null, + "format": "int32", + "type": "integer" + }, + "requestedAt": { + "description": "The timestamp when the batch process was initiated, in ISO 8601 format.", + "example": null, + "format": "date-time", + "type": "string" + }, + "results": { + "example": null, + "items": { + "$ref": "#/components/schemas/SimplePublicUpsertObject" + }, + "type": "array" + }, + "startedAt": { + "description": "The timestamp when the batch process began execution, in ISO 8601 format.", + "example": null, + "format": "date-time", + "type": "string" + }, + "status": { + "description": "The status of the batch processing request. Can be: \"PENDING\", \"PROCESSING\", \"CANCELLED\", or \"COMPLETE\".", + "enum": [ + "CANCELED", + "COMPLETE", + "PENDING", + "PROCESSING" + ], + "example": null, + "type": "string" + } + }, + "required": [ + "completedAt", + "results", + "startedAt", + "status" + ], + "type": "object" + }, + "CollectionResponseAssociatedId": { + "example": null, + "properties": { + "paging": { + "$ref": "#/components/schemas/Paging" + }, + "results": { + "example": null, + "items": { + "$ref": "#/components/schemas/AssociatedId" + }, + "type": "array" + } + }, + "required": [ + "results" + ], + "type": "object" + }, + "CollectionResponseSimplePublicObjectWithAssociationsForwardPaging": { + "example": null, + "properties": { + "paging": { + "$ref": "#/components/schemas/ForwardPaging" + }, + "results": { + "example": null, + "items": { + "$ref": "#/components/schemas/SimplePublicObjectWithAssociations" + }, + "type": "array" + } + }, + "required": [ + "results" + ], + "type": "object" + }, + "CollectionResponseWithTotalSimplePublicObject": { + "description": "Represents a list of simple objects returned from an API request, along with the total count of objects available.", + "example": null, + "properties": { + "paging": { + "$ref": "#/components/schemas/Paging" + }, + "results": { + "example": null, + "items": { + "$ref": "#/components/schemas/SimplePublicObject" + }, + "type": "array" + }, + "total": { + "description": "The number of available results", + "example": null, + "format": "int32", + "type": "integer" + } + }, + "required": [ + "results", + "total" + ], + "type": "object" + }, + "Error": { + "example": { + "category": "VALIDATION_ERROR", + "correlationId": "aeb5f871-7f07-4993-9211-075dc63e7cbf", + "links": { + "knowledge-base": "https://www.hubspot.com/products/service/knowledge-base" + }, + "message": "Invalid input (details will vary based on the error)" + }, + "properties": { + "category": { + "description": "The error category", + "example": null, + "type": "string" + }, + "context": { + "additionalProperties": { + "example": null, + "items": { + "example": null, + "type": "string" + }, + "type": "array" + }, + "description": "Context about the error condition", + "example": "{invalidPropertyName=[propertyValue], missingScopes=[scope1, scope2]}", + "type": "object" + }, + "correlationId": { + "description": "A unique identifier for the request. Include this value with any error reports or support tickets", + "example": "aeb5f871-7f07-4993-9211-075dc63e7cbf", + "format": "uuid", + "type": "string" + }, + "errors": { + "description": "further information about the error", + "example": null, + "items": { + "$ref": "#/components/schemas/ErrorDetail" + }, + "type": "array" + }, + "links": { + "additionalProperties": { + "example": null, + "type": "string" + }, + "description": "A map of link names to associated URIs containing documentation about the error or recommended remediation steps", + "example": null, + "type": "object" + }, + "message": { + "description": "A human readable message describing the error along with remediation steps where appropriate", + "example": "An error occurred", + "type": "string" + }, + "subCategory": { + "description": "A specific category that contains more specific detail about the error", + "example": null, + "type": "string" + } + }, + "required": [ + "category", + "correlationId", + "message" + ], + "type": "object" + }, + "ErrorDetail": { + "example": null, + "properties": { + "code": { + "description": "The status code associated with the error detail", + "example": null, + "type": "string" + }, + "context": { + "additionalProperties": { + "example": null, + "items": { + "example": null, + "type": "string" + }, + "type": "array" + }, + "description": "Context about the error condition", + "example": "{missingScopes=[scope1, scope2]}", + "type": "object" + }, + "in": { + "description": "The name of the field or parameter in which the error was found.", + "example": null, + "type": "string" + }, + "message": { + "description": "A human readable message describing the error along with remediation steps where appropriate", + "example": null, + "type": "string" + }, + "subCategory": { + "description": "A specific category that contains more specific detail about the error", + "example": null, + "type": "string" + } + }, + "required": [ + "message" + ], + "type": "object" + }, + "Filter": { + "description": "Defines a single condition for searching CRM objects, specifying the property to filter on, the operator to use (such as equals, greater than, or contains), and the value(s) to compare against. ", + "example": null, + "properties": { + "highValue": { + "description": "The upper boundary value when using ranged-based filters.", + "example": null, + "type": "string" + }, + "operator": { + "description": "The comparison operator used in the filter, such as \"EQ\" or \"GT\".", + "enum": [ + "BETWEEN", + "CONTAINS_TOKEN", + "EQ", + "GT", + "GTE", + "HAS_PROPERTY", + "IN", + "LT", + "LTE", + "NEQ", + "NOT_CONTAINS_TOKEN", + "NOT_HAS_PROPERTY", + "NOT_IN" + ], + "example": null, + "type": "string" + }, + "propertyName": { + "description": "The name of the property to apply the filter to.", + "example": null, + "type": "string" + }, + "value": { + "description": "The value to match against the property.", + "example": null, + "type": "string" + }, + "values": { + "description": "The values to match against the property.", + "example": null, + "items": { + "example": null, + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "operator", + "propertyName" + ], + "type": "object" + }, + "FilterGroup": { + "example": null, + "properties": { + "filters": { + "example": null, + "items": { + "$ref": "#/components/schemas/Filter" + }, + "type": "array" + } + }, + "required": [ + "filters" + ], + "type": "object" + }, + "ForwardPaging": { + "example": null, + "properties": { + "next": { + "$ref": "#/components/schemas/NextPage" + } + }, + "type": "object" + }, + "NextPage": { + "description": "Specifies the paging information needed to retrieve the next set of results in a paginated API response", + "example": { + "after": "NTI1Cg%3D%3D", + "link": "?after=NTI1Cg%3D%3D" + }, + "properties": { + "after": { + "description": "A paging cursor token for retrieving subsequent pages.", + "example": null, + "type": "string" + }, + "link": { + "description": "A URL that can be used to retrieve the next page results.", + "example": null, + "type": "string" + } + }, + "required": [ + "after" + ], + "type": "object" + }, + "Paging": { + "example": null, + "properties": { + "next": { + "$ref": "#/components/schemas/NextPage" + }, + "prev": { + "$ref": "#/components/schemas/PreviousPage" + } + }, + "type": "object" + }, + "PreviousPage": { + "description": "specifies the paging information needed to retrieve the previous set of results in a paginated API response", + "example": null, + "properties": { + "before": { + "description": "A paging cursor token for retrieving previous pages.", + "example": null, + "type": "string" + }, + "link": { + "description": "A URL that can be used to retrieve the previous pages' results.", + "example": null, + "type": "string" + } + }, + "required": [ + "before" + ], + "type": "object" + }, + "PublicAssociationsForObject": { + "example": null, + "properties": { + "to": { + "$ref": "#/components/schemas/PublicObjectId" + }, + "types": { + "example": null, + "items": { + "$ref": "#/components/schemas/AssociationSpec" + }, + "type": "array" + } + }, + "required": [ + "to", + "types" + ], + "type": "object" + }, + "PublicGdprDeleteInput": { + "description": "An input that contains the information required to process a public GDPR data deletion request.", + "example": null, + "properties": { + "idProperty": { + "description": "The name of a property whose values are unique for this object. An alternative to identifying a contact by ID.", + "example": null, + "type": "string" + }, + "objectId": { + "description": "The ID of the contact to permanently delete.", + "example": null, + "type": "string" + } + }, + "required": [ + "objectId" + ], + "type": "object" + }, + "PublicMergeInput": { + "description": "Input data for merging two records.", + "example": null, + "properties": { + "objectIdToMerge": { + "description": "The object ID of the record that the merge will not set as the current value after the merge.", + "example": null, + "type": "string" + }, + "primaryObjectId": { + "description": "The object ID of the record that the merge will generally set as the current value after the merge.", + "example": null, + "type": "string" + } + }, + "required": [ + "objectIdToMerge", + "primaryObjectId" + ], + "type": "object" + }, + "PublicObjectId": { + "description": "Contains the Id of a Public Object", + "example": null, + "properties": { + "id": { + "description": "The unique ID of the object.", + "example": null, + "type": "string" + } + }, + "required": [ + "id" + ], + "type": "object" + }, + "PublicObjectSearchRequest": { + "description": "Describes a search request", + "example": null, + "properties": { + "after": { + "description": "A paging cursor token for retrieving subsequent pages.", + "example": null, + "type": "string" + }, + "filterGroups": { + "description": "Up to 6 groups of filters defining additional query criteria.", + "example": null, + "items": { + "$ref": "#/components/schemas/FilterGroup" + }, + "type": "array" + }, + "limit": { + "description": "The maximum results to return, up to 200 objects.", + "example": null, + "format": "int32", + "type": "integer" + }, + "properties": { + "description": "A list of property names to include in the response.", + "example": null, + "items": { + "example": null, + "type": "string" + }, + "type": "array" + }, + "query": { + "description": "The search query string, up to 3000 characters.", + "example": null, + "type": "string" + }, + "sorts": { + "description": "Specifies sorting order based on object properties.", + "example": null, + "items": { + "example": null, + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "after", + "filterGroups", + "limit", + "properties", + "sorts" + ], + "type": "object" + }, + "PublicObjectWarning": { + "example": null, + "properties": { + "category": { + "example": null, + "type": "string" + }, + "context": { + "additionalProperties": { + "example": null, + "type": "string" + }, + "example": null, + "type": "object" + }, + "message": { + "example": null, + "type": "string" + } + }, + "required": [ + "category", + "context", + "message" + ], + "type": "object" + }, + "SimplePublicObject": { + "description": "A simple public object.", + "example": { + "archived": false, + "createdAt": "2019-10-30T03:30:17.883Z", + "id": "512", + "properties": { + "property_checkbox": "false", + "property_date": "1572480000000", + "property_dropdown": "choice_b", + "property_multiple_checkboxes": "chocolate;strawberry", + "property_number": "17", + "property_radio": "option_1", + "property_string": "value" + }, + "updatedAt": "2019-12-07T16:50:06.678Z" + }, + "properties": { + "archived": { + "description": "Whether the object is archived.", + "example": null, + "type": "boolean" + }, + "archivedAt": { + "description": "The timestamp when the object was archived, in ISO 8601 format.", + "example": null, + "format": "date-time", + "type": "string" + }, + "createdAt": { + "description": "The timestamp when the object was created, in ISO 8601 format.", + "example": null, + "format": "date-time", + "type": "string" + }, + "id": { + "description": "The unique ID of the object.", + "example": null, + "type": "string" + }, + "objectWriteTraceId": { + "description": "An identifier used for tracing the write request for the object.", + "example": null, + "type": "string" + }, + "properties": { + "additionalProperties": { + "example": null, + "type": "string" + }, + "description": "Key-value pairs representing the properties of the object.", + "example": null, + "type": "object" + }, + "propertiesWithHistory": { + "additionalProperties": { + "example": null, + "items": { + "$ref": "#/components/schemas/ValueWithTimestamp" + }, + "type": "array" + }, + "description": "Key-value pairs representing the properties of the object along with their history.", + "example": null, + "type": "object" + }, + "updatedAt": { + "description": "The timestamp when the object was last updated, in ISO 8601 format.", + "example": null, + "format": "date-time", + "type": "string" + }, + "url": { + "description": "The URL associated with the object.", + "example": null, + "type": "string" + }, + "warnings": { + "example": null, + "items": { + "$ref": "#/components/schemas/PublicObjectWarning" + }, + "type": "array" + } + }, + "required": [ + "archived", + "createdAt", + "id", + "properties", + "updatedAt" + ], + "type": "object" + }, + "SimplePublicObjectBatchInput": { + "description": "Contains an array of CRM object records to be processed in a batch operation, each defined by their ID and properties.", + "example": null, + "properties": { + "id": { + "description": "The ID of the contact to update. This can be the object ID, or the unique property value of the `idProperty` property.", + "example": null, + "type": "string" + }, + "idProperty": { + "description": "The name of a unique property, when identifying records by property. ", + "example": "my_unique_property_name", + "type": "string" + }, + "objectWriteTraceId": { + "description": "A unique identifier for tracing the request.", + "example": null, + "type": "string" + }, + "properties": { + "additionalProperties": { + "example": null, + "type": "string" + }, + "description": "Key-value pairs representing the properties of the object.", + "example": null, + "type": "object" + } + }, + "required": [ + "id", + "properties" + ], + "type": "object" + }, + "SimplePublicObjectBatchInputForCreate": { + "description": "An input object that contains a collection of objects to be created together in a batch.", + "example": null, + "properties": { + "associations": { + "example": null, + "items": { + "$ref": "#/components/schemas/PublicAssociationsForObject" + }, + "type": "array" + }, + "objectWriteTraceId": { + "description": "An identifier used for tracing the creation or update request of the object.", + "example": null, + "type": "string" + }, + "properties": { + "additionalProperties": { + "example": null, + "type": "string" + }, + "description": "Key value pairs representing the properties of the object.", + "example": null, + "type": "object" + } + }, + "required": [ + "associations", + "properties" + ], + "type": "object" + }, + "SimplePublicObjectBatchInputUpsert": { + "description": "Represents an object used in batch upsert operations, containing an object\u2019s unique identifier, its properties, and optionally the unique property name and a write trace ID.", + "example": null, + "properties": { + "id": { + "description": "The unique ID of the object.", + "example": null, + "type": "string" + }, + "idProperty": { + "description": "The name of a property whose values are unique for this object", + "example": null, + "type": "string" + }, + "objectWriteTraceId": { + "description": "An identifier for tracing the creation request.", + "example": null, + "type": "string" + }, + "properties": { + "additionalProperties": { + "example": null, + "type": "string" + }, + "description": "Key value pairs representing the properties of the object.", + "example": null, + "type": "object" + } + }, + "required": [ + "id", + "properties" + ], + "type": "object" + }, + "SimplePublicObjectId": { + "description": "Contains the Id of a Public Object", + "example": null, + "properties": { + "id": { + "description": "The unique ID of the object.", + "example": "430001", + "type": "string" + } + }, + "required": [ + "id" + ], + "type": "object" + }, + "SimplePublicObjectInput": { + "description": "Represents the input required to create or update a CRM object, containing an object with property names and their corresponding values.", + "example": { + "properties": { + "property_checkbox": "false", + "property_date": "1572480000000", + "property_dropdown": "choice_b", + "property_multiple_checkboxes": "chocolate;strawberry", + "property_number": "17", + "property_radio": "option_1", + "property_string": "value" + } + }, + "properties": { + "properties": { + "additionalProperties": { + "example": null, + "type": "string" + }, + "description": "Key value pairs representing the properties of the object.", + "example": null, + "type": "object" + } + }, + "required": [ + "properties" + ], + "type": "object" + }, + "SimplePublicObjectInputForCreate": { + "description": "Is the input object used to create a new CRM object, containing the properties to be set and optional associations to link the new record with other CRM objects.", + "example": null, + "properties": { + "associations": { + "example": null, + "items": { + "$ref": "#/components/schemas/PublicAssociationsForObject" + }, + "type": "array" + }, + "properties": { + "additionalProperties": { + "example": null, + "type": "string" + }, + "description": "Key-value pairs for setting properties for the new object.", + "example": null, + "type": "object" + } + }, + "required": [ + "associations", + "properties" + ], + "type": "object" + }, + "SimplePublicObjectWithAssociations": { + "description": "Represents a CRM object along with its properties, timestamps, and a set of associated object IDs grouped by association type.", + "example": null, + "properties": { + "archived": { + "description": "Whether the object is archived.", + "example": null, + "type": "boolean" + }, + "archivedAt": { + "description": "The timestamp when the object was archived, in ISO 8601 format.", + "example": null, + "format": "date-time", + "type": "string" + }, + "associations": { + "additionalProperties": { + "$ref": "#/components/schemas/CollectionResponseAssociatedId" + }, + "description": "A list defining relationships with other objects.", + "example": null, + "type": "object" + }, + "createdAt": { + "description": "The timestamp when the object was created, in ISO 8601 format.", + "example": null, + "format": "date-time", + "type": "string" + }, + "id": { + "description": "The unique ID of the object.", + "example": null, + "type": "string" + }, + "objectWriteTraceId": { + "description": "An identifier used for tracing the creation or update request of the object.", + "example": null, + "type": "string" + }, + "properties": { + "additionalProperties": { + "example": null, + "type": "string" + }, + "description": "Key value pairs representing the properties of the object.", + "example": null, + "type": "object" + }, + "propertiesWithHistory": { + "additionalProperties": { + "example": null, + "items": { + "$ref": "#/components/schemas/ValueWithTimestamp" + }, + "type": "array" + }, + "description": "Key-value pairs representing the properties of the object along with their history.", + "example": null, + "type": "object" + }, + "updatedAt": { + "description": "The timestamp when the object was last updated, in ISO 8601 format.", + "example": null, + "format": "date-time", + "type": "string" + }, + "url": { + "description": "The URL on the API that provide direct navigation to the corresponding UI pages for the connectors.", + "example": null, + "type": "string" + }, + "warnings": { + "example": null, + "items": { + "$ref": "#/components/schemas/PublicObjectWarning" + }, + "type": "array" + } + }, + "required": [ + "archived", + "createdAt", + "id", + "properties", + "updatedAt" + ], + "type": "object" + }, + "SimplePublicUpsertObject": { + "description": "Represents a CRM object that has either been created or updated (upserted)", + "example": null, + "properties": { + "archived": { + "description": "Whether the object is archived.", + "example": null, + "type": "boolean" + }, + "archivedAt": { + "description": "The timestamp when the object was archived, in ISO 8601 format.", + "example": null, + "format": "date-time", + "type": "string" + }, + "createdAt": { + "description": "The timestamp when the object was created, in ISO 8601 format.", + "example": null, + "format": "date-time", + "type": "string" + }, + "id": { + "description": "The unique ID of the object.", + "example": null, + "type": "string" + }, + "new": { + "description": "Whether the property is new.", + "example": null, + "type": "boolean" + }, + "objectWriteTraceId": { + "description": "An identifier for tracing the creation request.", + "example": null, + "type": "string" + }, + "properties": { + "additionalProperties": { + "example": null, + "type": "string" + }, + "description": "Key value pairs representing the properties of the object.", + "example": null, + "type": "object" + }, + "propertiesWithHistory": { + "additionalProperties": { + "example": null, + "items": { + "$ref": "#/components/schemas/ValueWithTimestamp" + }, + "type": "array" + }, + "description": "Key-value pairs representing the properties of the object along with their history.", + "example": null, + "type": "object" + }, + "updatedAt": { + "description": "The timestamp when the object was last updated, in ISO 8601 format.", + "example": null, + "format": "date-time", + "type": "string" + }, + "url": { + "description": "The URL associated with the object.", + "example": null, + "type": "string" + }, + "warnings": { + "example": null, + "items": { + "$ref": "#/components/schemas/PublicObjectWarning" + }, + "type": "array" + } + }, + "required": [ + "archived", + "createdAt", + "id", + "new", + "properties", + "updatedAt" + ], + "type": "object" + }, + "StandardError": { + "description": "Represents a standard error response in the HubSpot API, providing detailed information about an error that occurred during an API request.", + "example": null, + "properties": { + "category": { + "description": "The main category of the error.", + "example": null, + "type": "string" + }, + "context": { + "additionalProperties": { + "example": null, + "items": { + "example": null, + "type": "string" + }, + "type": "array" + }, + "description": "Additional context-specific information related to the error.", + "example": null, + "type": "object" + }, + "errors": { + "description": "The detailed error objects.", + "example": null, + "items": { + "$ref": "#/components/schemas/ErrorDetail" + }, + "type": "array" + }, + "id": { + "description": "A unique ID for the error instance.", + "example": null, + "type": "string" + }, + "links": { + "additionalProperties": { + "example": null, + "type": "string" + }, + "description": "URLs linking to documentation or resources associated with the error.", + "example": null, + "type": "object" + }, + "message": { + "description": "A human-readable string describing the error and possible remediation steps.", + "example": null, + "type": "string" + }, + "status": { + "description": "The HTTP status code associated with the error.", + "example": null, + "type": "string" + }, + "subCategory": { + "description": "A more specific error category within each main category.", + "example": null, + "properties": {}, + "type": "object" + } + }, + "required": [ + "category", + "context", + "errors", + "links", + "message", + "status" + ], + "type": "object" + }, + "ValueWithTimestamp": { + "description": "Property model that includes timestamp.", + "example": null, + "properties": { + "sourceId": { + "description": "The unique ID of the property.", + "example": null, + "type": "string" + }, + "sourceLabel": { + "description": "A human-readable label.", + "example": null, + "type": "string" + }, + "sourceType": { + "description": "The property type.", + "example": null, + "type": "string" + }, + "timestamp": { + "description": "The timestamp when the property was updated, in ISO 8601 format.", + "example": null, + "format": "date-time", + "type": "string" + }, + "updatedByUserId": { + "description": "The ID of the user who last updated the property.", + "example": null, + "format": "int32", + "type": "integer" + }, + "value": { + "description": "The property value.", + "example": null, + "type": "string" + } + }, + "required": [ + "sourceType", + "timestamp", + "value" + ], + "type": "object" + } + }, + "securitySchemes": { + "developer_hapikey": { + "in": "query", + "name": "hapikey", + "type": "apiKey" + }, + "oauth2": { + "flows": { + "authorizationCode": { + "authorizationUrl": "https://app.hubspot.com/oauth/authorize", + "scopes": { + "crm.objects.commercepayments.read": "", + "crm.objects.contacts.read": "", + "crm.objects.contacts.write": "", + "oauth": "" + }, + "tokenUrl": "https://api.hubapi.com/oauth/v1/token" + } + }, + "type": "oauth2" + }, + "private_apps": { + "in": "header", + "name": "private-app", + "type": "apiKey" + }, + "private_apps_legacy": { + "in": "header", + "name": "private-app-legacy", + "type": "apiKey" + } + } + }, + "info": { + "description": "CRM objects such as companies, contacts, deals, line items, products, tickets, and quotes are standard objects in HubSpot\u2019s CRM. These core building blocks support custom properties, store critical information, and play a central role in the HubSpot application.\n\n## Supported Object Types\n\nThis API provides access to collections of CRM objects, which return a map of property names to values. Each object type has its own set of default properties, which can be found by exploring the [CRM Object Properties API](https://developers.hubspot.com/docs/methods/crm-properties/crm-properties-overview).\n\n|Object Type |Properties returned by default |\n|--|--|\n| `companies` | `name`, `domain` |\n| `contacts` | `firstname`, `lastname`, `email` |\n| `deals` | `dealname`, `amount`, `closedate`, `pipeline`, `dealstage` |\n| `products` | `name`, `description`, `price` |\n| `tickets` | `content`, `hs_pipeline`, `hs_pipeline_stage`, `hs_ticket_category`, `hs_ticket_priority`, `subject` |\n\nFind a list of all properties for an object type using the [CRM Object Properties](https://developers.hubspot.com/docs/methods/crm-properties/get-properties) API. e.g. `GET https://api.hubapi.com/properties/v2/companies/properties`. Change the properties returned in the response using the `properties` array in the request body.", + "title": "Contacts", + "version": "v3", + "x-hubspot-api-use-case": "Retrieve a contact by ID to bring that contact data into your external system.", + "x-hubspot-introduction": "Use the contacts API to create and manage contacts.", + "x-hubspot-product-tier-requirements": { + "cms": "FREE", + "commerce": "FREE", + "crmHub": "FREE", + "dataHub": "FREE", + "marketing": "FREE", + "sales": "FREE", + "service": "FREE" + }, + "x-hubspot-related-documentation": [ + { + "name": "Contacts Guide", + "url": "https://developers.hubspot.com/docs/guides/api/crm/objects/contacts" + } + ] + }, + "openapi": "3.0.1", + "paths": { + "/crm/v3/objects/contacts": { + "get": { + "description": "Retrieve all contacts, using query parameters to specify the information that gets returned.", + "operationId": "get-/crm/v3/objects/contacts_getPage", + "parameters": [ + { + "description": "The paging cursor token of the last successfully read resource will be returned as the `paging.next.after` JSON property of a paged response containing more results.", + "explode": true, + "in": "query", + "name": "after", + "required": false, + "schema": { + "example": null, + "type": "string" + }, + "style": "form" + }, + { + "description": "Whether to return only results that have been archived.", + "explode": true, + "in": "query", + "name": "archived", + "required": false, + "schema": { + "default": false, + "example": null, + "type": "boolean" + }, + "style": "form" + }, + { + "description": "A comma separated list of object types to retrieve associated IDs for. If any of the specified associations do not exist, they will be ignored.", + "explode": true, + "in": "query", + "name": "associations", + "required": false, + "schema": { + "example": null, + "items": { + "example": null, + "type": "string" + }, + "type": "array" + }, + "style": "form" + }, + { + "description": "The maximum number of results to display per page.", + "explode": true, + "in": "query", + "name": "limit", + "required": false, + "schema": { + "default": 10, + "example": null, + "format": "int32", + "type": "integer" + }, + "style": "form" + }, + { + "description": "A comma separated list of the properties to be returned in the response. If any of the specified properties are not present on the requested object(s), they will be ignored.", + "explode": true, + "in": "query", + "name": "properties", + "required": false, + "schema": { + "example": null, + "items": { + "example": null, + "type": "string" + }, + "type": "array" + }, + "style": "form" + }, + { + "description": "A comma separated list of the properties to be returned along with their history of previous values. If any of the specified properties are not present on the requested object(s), they will be ignored. Usage of this parameter will reduce the maximum number of contacts that can be read by a single request.", + "explode": true, + "in": "query", + "name": "propertiesWithHistory", + "required": false, + "schema": { + "example": null, + "items": { + "example": null, + "type": "string" + }, + "type": "array" + }, + "style": "form" + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "example": null, + "schema": { + "$ref": "#/components/schemas/CollectionResponseSimplePublicObjectWithAssociationsForwardPaging" + } + } + }, + "description": "successful operation" + }, + "default": { + "$ref": "#/components/responses/Error", + "description": "" + } + }, + "security": [ + { + "oauth2": [ + "crm.objects.contacts.read" + ] + } + ], + "summary": "Retrieve contacts", + "tags": [ + "Basic" + ] + }, + "post": { + "description": "Create a single contact. Include a `properties` object to define [property values](https://developers.hubspot.com/docs/guides/api/crm/properties) for the contact, along with an `associations` array to define [associations](https://developers.hubspot.com/docs/guides/api/crm/associations/associations-v4) with other CRM records.", + "operationId": "post-/crm/v3/objects/contacts_create", + "parameters": [], + "requestBody": { + "content": { + "application/json": { + "example": null, + "schema": { + "$ref": "#/components/schemas/SimplePublicObjectInputForCreate" + } + } + }, + "required": true + }, + "responses": { + "201": { + "content": { + "application/json": { + "example": null, + "schema": { + "$ref": "#/components/schemas/SimplePublicObject" + } + } + }, + "description": "successful operation" + }, + "default": { + "$ref": "#/components/responses/Error", + "description": "" + } + }, + "security": [ + { + "oauth2": [ + "crm.objects.contacts.write" + ] + } + ], + "summary": "Create a contact", + "tags": [ + "Basic" + ] + } + }, + "/crm/v3/objects/contacts/batch/archive": { + "post": { + "description": "Archive a batch of contacts by ID. Archived contacts can be restored within 90 days of deletion. Learn more about the [data impacted by contact deletions](https://knowledge.hubspot.com/privacy-and-consent/understand-restorable-and-permanent-contact-deletions) and how to [restore archived records](https://knowledge.hubspot.com/records/restore-deleted-records).", + "operationId": "post-/crm/v3/objects/contacts/batch/archive_archive", + "parameters": [], + "requestBody": { + "content": { + "application/json": { + "example": null, + "schema": { + "$ref": "#/components/schemas/BatchInputSimplePublicObjectId" + } + } + }, + "required": true + }, + "responses": { + "204": { + "content": {}, + "description": "No content" + }, + "default": { + "$ref": "#/components/responses/Error", + "description": "" + } + }, + "security": [ + { + "oauth2": [ + "crm.objects.contacts.write" + ] + } + ], + "summary": "Archive a batch of contacts", + "tags": [ + "Batch" + ] + } + }, + "/crm/v3/objects/contacts/batch/create": { + "post": { + "description": "Create a batch of contacts. The `inputs` array can contain a `properties` object to define property values for each record, along with an `associations` array to define [associations](https://developers.hubspot.com/docs/guides/api/crm/associations/associations-v4) with other CRM records.", + "operationId": "post-/crm/v3/objects/contacts/batch/create_create", + "parameters": [], + "requestBody": { + "content": { + "application/json": { + "example": null, + "schema": { + "$ref": "#/components/schemas/BatchInputSimplePublicObjectBatchInputForCreate" + } + } + }, + "required": true + }, + "responses": { + "201": { + "content": { + "application/json": { + "example": null, + "schema": { + "$ref": "#/components/schemas/BatchResponseSimplePublicObject" + } + } + }, + "description": "successful operation" + }, + "207": { + "content": { + "application/json": { + "example": null, + "schema": { + "$ref": "#/components/schemas/BatchResponseSimplePublicObjectWithErrors" + } + } + }, + "description": "multiple statuses" + }, + "default": { + "$ref": "#/components/responses/Error", + "description": "" + } + }, + "security": [ + { + "oauth2": [ + "crm.objects.contacts.write" + ] + } + ], + "summary": "Create a batch of contacts", + "tags": [ + "Batch" + ] + } + }, + "/crm/v3/objects/contacts/batch/read": { + "post": { + "description": "Retrieve a batch of contacts by ID (`contactId`) or unique property value (`idProperty`). ", + "operationId": "post-/crm/v3/objects/contacts/batch/read_read", + "parameters": [ + { + "description": "Whether to return only results that have been archived.", + "explode": true, + "in": "query", + "name": "archived", + "required": false, + "schema": { + "default": false, + "example": null, + "type": "boolean" + }, + "style": "form" + } + ], + "requestBody": { + "content": { + "application/json": { + "example": null, + "schema": { + "$ref": "#/components/schemas/BatchReadInputSimplePublicObjectId" + } + } + }, + "required": true + }, + "responses": { + "200": { + "content": { + "application/json": { + "example": null, + "schema": { + "$ref": "#/components/schemas/BatchResponseSimplePublicObject" + } + } + }, + "description": "successful operation" + }, + "207": { + "content": { + "application/json": { + "example": null, + "schema": { + "$ref": "#/components/schemas/BatchResponseSimplePublicObjectWithErrors" + } + } + }, + "description": "multiple statuses" + }, + "default": { + "$ref": "#/components/responses/Error", + "description": "" + } + }, + "security": [ + { + "oauth2": [ + "crm.objects.contacts.read" + ] + } + ], + "summary": "Retrieve a batch of contacts", + "tags": [ + "Batch" + ] + } + }, + "/crm/v3/objects/contacts/batch/update": { + "post": { + "description": "Update a batch of contacts by ID (`contactId`) or unique property value (`idProperty`). Provided property values will be overwritten. Read-only and non-existent properties will result in an error. Properties values can be cleared by passing an empty string.", + "operationId": "post-/crm/v3/objects/contacts/batch/update_update", + "parameters": [], + "requestBody": { + "content": { + "application/json": { + "example": null, + "schema": { + "$ref": "#/components/schemas/BatchInputSimplePublicObjectBatchInput" + } + } + }, + "required": true + }, + "responses": { + "200": { + "content": { + "application/json": { + "example": null, + "schema": { + "$ref": "#/components/schemas/BatchResponseSimplePublicObject" + } + } + }, + "description": "successful operation" + }, + "207": { + "content": { + "application/json": { + "example": null, + "schema": { + "$ref": "#/components/schemas/BatchResponseSimplePublicObjectWithErrors" + } + } + }, + "description": "multiple statuses" + }, + "default": { + "$ref": "#/components/responses/Error", + "description": "" + } + }, + "security": [ + { + "oauth2": [ + "crm.objects.contacts.write" + ] + } + ], + "summary": "Update a batch of contacts", + "tags": [ + "Batch" + ] + } + }, + "/crm/v3/objects/contacts/batch/upsert": { + "post": { + "description": "Upsert a batch of contacts. The `inputs` array can contain a `properties` object to define property values for each record.", + "operationId": "post-/crm/v3/objects/contacts/batch/upsert_upsert", + "parameters": [], + "requestBody": { + "content": { + "application/json": { + "example": null, + "schema": { + "$ref": "#/components/schemas/BatchInputSimplePublicObjectBatchInputUpsert" + } + } + }, + "required": true + }, + "responses": { + "200": { + "content": { + "application/json": { + "example": null, + "schema": { + "$ref": "#/components/schemas/BatchResponseSimplePublicUpsertObject" + } + } + }, + "description": "successful operation" + }, + "207": { + "content": { + "application/json": { + "example": null, + "schema": { + "$ref": "#/components/schemas/BatchResponseSimplePublicUpsertObjectWithErrors" + } + } + }, + "description": "multiple statuses" + }, + "default": { + "$ref": "#/components/responses/Error", + "description": "" + } + }, + "security": [ + { + "oauth2": [ + "crm.objects.contacts.write" + ] + } + ], + "summary": "Create or update a batch of contacts", + "tags": [ + "Batch" + ] + } + }, + "/crm/v3/objects/contacts/gdpr-delete": { + "post": { + "description": "Permanently delete a contact and all associated content to follow GDPR. Use optional property `idProperty` set to `email` to identify contact by email address. If email address is not found, the email address will be added to a blocklist and prevent it from being used in the future. Learn more about [permanently deleting contacts](https://knowledge.hubspot.com/privacy-and-consent/how-do-i-perform-a-gdpr-delete-in-hubspot).", + "operationId": "post-/crm/v3/objects/contacts/gdpr-delete_purge", + "parameters": [], + "requestBody": { + "content": { + "application/json": { + "example": null, + "schema": { + "$ref": "#/components/schemas/PublicGdprDeleteInput" + } + } + }, + "required": true + }, + "responses": { + "204": { + "content": {}, + "description": "No content" + }, + "default": { + "$ref": "#/components/responses/Error", + "description": "" + } + }, + "security": [ + { + "oauth2": [ + "crm.objects.contacts.write" + ] + } + ], + "summary": "Permanently delete a contact (GDPR-compliant)", + "tags": [ + "Advanced" + ] + } + }, + "/crm/v3/objects/contacts/merge": { + "post": { + "description": "Merge two contact records. Learn more about [merging records](https://knowledge.hubspot.com/records/merge-records). ", + "operationId": "post-/crm/v3/objects/contacts/merge_merge", + "parameters": [], + "requestBody": { + "content": { + "application/json": { + "example": null, + "schema": { + "$ref": "#/components/schemas/PublicMergeInput" + } + } + }, + "required": true + }, + "responses": { + "200": { + "content": { + "application/json": { + "example": null, + "schema": { + "$ref": "#/components/schemas/SimplePublicObject" + } + } + }, + "description": "successful operation" + }, + "default": { + "$ref": "#/components/responses/Error", + "description": "" + } + }, + "security": [ + { + "oauth2": [ + "crm.objects.contacts.write" + ] + } + ], + "summary": "Merge two contacts", + "tags": [ + "Advanced" + ] + } + }, + "/crm/v3/objects/contacts/search": { + "post": { + "description": "Search for contacts by filtering on properties, searching through associations, and sorting results. Learn more about [CRM search](https://developers.hubspot.com/docs/guides/api/crm/search#make-a-search-request).", + "operationId": "post-/crm/v3/objects/contacts/search_doSearch", + "parameters": [], + "requestBody": { + "content": { + "application/json": { + "example": null, + "schema": { + "$ref": "#/components/schemas/PublicObjectSearchRequest" + } + } + }, + "required": true + }, + "responses": { + "200": { + "content": { + "application/json": { + "example": null, + "schema": { + "$ref": "#/components/schemas/CollectionResponseWithTotalSimplePublicObject" + } + } + }, + "description": "successful operation" + }, + "default": { + "$ref": "#/components/responses/Error", + "description": "" + } + }, + "security": [ + { + "oauth2": [ + "crm.objects.contacts.read" + ] + } + ], + "summary": "Search for contacts", + "tags": [ + "Search" + ], + "x-hubspot-rate-limit-exemptions": [ + "ten-secondly" + ] + } + }, + "/crm/v3/objects/contacts/{contactId}": { + "delete": { + "description": "Delete a contact by ID. Deleted contacts can be restored within 90 days of deletion. Learn more about the [data impacted by contact deletions](https://knowledge.hubspot.com/privacy-and-consent/understand-restorable-and-permanent-contact-deletions) and how to [restore archived records](https://knowledge.hubspot.com/records/restore-deleted-records).", + "operationId": "delete-/crm/v3/objects/contacts/{contactId}_archive", + "parameters": [ + { + "in": "path", + "name": "contactId", + "required": true, + "schema": { + "example": null, + "pattern": ".+", + "type": "string" + } + } + ], + "responses": { + "204": { + "content": {}, + "description": "No content" + }, + "default": { + "$ref": "#/components/responses/Error", + "description": "" + } + }, + "security": [ + { + "oauth2": [ + "crm.objects.contacts.write" + ] + } + ], + "summary": "Archive a contact", + "tags": [ + "Basic" + ] + }, + "get": { + "description": "Retrieve a contact by its ID (`contactId`) or by a unique property (`idProperty`). You can specify what is returned using the `properties` query parameter.", + "operationId": "get-/crm/v3/objects/contacts/{contactId}_getById", + "parameters": [ + { + "in": "path", + "name": "contactId", + "required": true, + "schema": { + "example": null, + "pattern": ".+", + "type": "string" + } + }, + { + "description": "Whether to return only results that have been archived.", + "explode": true, + "in": "query", + "name": "archived", + "required": false, + "schema": { + "default": false, + "example": null, + "type": "boolean" + }, + "style": "form" + }, + { + "description": "A comma separated list of object types to retrieve associated IDs for. If any of the specified associations do not exist, they will be ignored.", + "explode": true, + "in": "query", + "name": "associations", + "required": false, + "schema": { + "example": null, + "items": { + "example": null, + "type": "string" + }, + "type": "array" + }, + "style": "form" + }, + { + "description": "The name of a property whose values are unique for this object type", + "explode": true, + "in": "query", + "name": "idProperty", + "required": false, + "schema": { + "example": null, + "type": "string" + }, + "style": "form" + }, + { + "description": "A comma separated list of the properties to be returned in the response. If any of the specified properties are not present on the requested object(s), they will be ignored.", + "explode": true, + "in": "query", + "name": "properties", + "required": false, + "schema": { + "example": null, + "items": { + "example": null, + "type": "string" + }, + "type": "array" + }, + "style": "form" + }, + { + "description": "A comma separated list of the properties to be returned along with their history of previous values. If any of the specified properties are not present on the requested object(s), they will be ignored.", + "explode": true, + "in": "query", + "name": "propertiesWithHistory", + "required": false, + "schema": { + "example": null, + "items": { + "example": null, + "type": "string" + }, + "type": "array" + }, + "style": "form" + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "example": null, + "schema": { + "$ref": "#/components/schemas/SimplePublicObjectWithAssociations" + } + } + }, + "description": "successful operation" + }, + "default": { + "$ref": "#/components/responses/Error", + "description": "" + } + }, + "security": [ + { + "oauth2": [ + "crm.objects.contacts.read" + ] + } + ], + "summary": "Retrieve a contact", + "tags": [ + "Basic" + ] + }, + "patch": { + "description": "Update an existing contact, identified by ID or email/unique property value. To identify a contact by ID, include the ID in the request URL path. To identify a contact by their email or other unique property, include the email/property value in the request URL path, and add the `idProperty` query parameter (`/crm/v3/objects/contacts/jon@website.com?idProperty=email`). Provided property values will be overwritten. Read-only and non-existent properties will result in an error. Properties values can be cleared by passing an empty string.", + "operationId": "patch-/crm/v3/objects/contacts/{contactId}_update", + "parameters": [ + { + "in": "path", + "name": "contactId", + "required": true, + "schema": { + "example": null, + "pattern": ".+", + "type": "string" + } + }, + { + "description": "The name of a property whose values are unique for this object type", + "explode": true, + "in": "query", + "name": "idProperty", + "required": false, + "schema": { + "example": null, + "type": "string" + }, + "style": "form" + } + ], + "requestBody": { + "content": { + "application/json": { + "example": null, + "schema": { + "$ref": "#/components/schemas/SimplePublicObjectInput" + } + } + }, + "required": true + }, + "responses": { + "200": { + "content": { + "application/json": { + "example": null, + "schema": { + "$ref": "#/components/schemas/SimplePublicObject" + } + } + }, + "description": "successful operation" + }, + "default": { + "$ref": "#/components/responses/Error", + "description": "" + } + }, + "security": [ + { + "oauth2": [ + "crm.objects.contacts.write" + ] + } + ], + "summary": "Update a contact", + "tags": [ + "Basic" + ] + } + } + }, + "servers": [ + { + "url": "https://api.hubapi.com" + } + ], + "tags": [ + { + "name": "Advanced" + }, + { + "name": "Basic" + }, + { + "name": "Batch" + }, + { + "name": "Search" + } + ], + "x-hubspot-available-client-libraries": [ + "Node", + "Python", + "Ruby", + "PHP" + ], + "x-hubspot-product-tier-requirements": { + "cms": "FREE", + "commerce": "FREE", + "crmHub": "FREE", + "dataHub": "FREE", + "marketing": "FREE", + "sales": "FREE", + "service": "FREE" + } +} diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..086fbda --- /dev/null +++ b/requirements.txt @@ -0,0 +1,7 @@ +# a2a-pack is installed by the platform base image. +deepagents>=0.5.0 +langchain>=0.3 +langchain-openai>=0.2 +langchain-core>=0.3 +langgraph>=0.6 +httpx>=0.27