# HubSpot OAuth

Connect HubSpot CRM contacts, companies, deals, tickets, and automation.

JsWorkflows provides a platform-managed HubSpot OAuth app. You do not need to create a HubSpot app or provide credentials.

## Available scopes

| Resource | Operation | Scope |
| --- | --- | --- |
| Contacts | Read contacts | `crm.objects.contacts.read` |
| Contacts | Create/update/delete contacts | `crm.objects.contacts.read`, `crm.objects.contacts.write` |
| Companies | Read companies | `crm.objects.companies.read` |
| Companies | Create/update/delete companies | `crm.objects.companies.read`, `crm.objects.companies.write` |
| Deals | Read deals | `crm.objects.deals.read` |
| Deals | Create/update/delete deals | `crm.objects.deals.read`, `crm.objects.deals.write` |
| Marketing Email | Read/send marketing emails | `marketing-email` |
| Files | Access file manager | `files` |
| Tickets | Read/manage tickets | `tickets` |
| Automation | Manage workflows & sequences | `automation` |

## Connecting

1. Go to **OAuth Connections → Add Connection → HubSpot**.
2. Select the resources and operations your workflow needs.
3. Sign in with your HubSpot account and grant the requested permissions.
4. Give the connection a handle (e.g., `my-hubspot`).

Use lowercase letters, numbers, and hyphens only for the handle.

Access tokens are refreshed automatically when they expire.

## Example: create a contact from a Shopify order

```js
export class Workflow {
  async start(data, _headers, api) {
    const { token, error } = await api.getOAuthToken('my-hubspot');
    if (error || !token) throw new Error(error || 'Missing HubSpot token');

    const customer = data.customer;

    const response = await fetch('https://api.hubapi.com/crm/v3/objects/contacts', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json; charset=utf-8',
        Authorization: `Bearer ${token}`,
      },
      body: JSON.stringify({
        properties: {
          email: customer.email,
          firstname: customer.first_name,
          lastname: customer.last_name,
        },
      }),
    });

    if (!response.ok) {
      throw new Error(`HubSpot API ${response.status}: ${await response.text()}`);
    }
  }
}
```

## Example: create a deal

```js
const { token, error } = await api.getOAuthToken('my-hubspot');
if (error || !token) throw new Error(error || 'Missing HubSpot token');

const response = await fetch('https://api.hubapi.com/crm/v3/objects/deals', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json; charset=utf-8',
    Authorization: `Bearer ${token}`,
  },
  body: JSON.stringify({
    properties: {
      dealname: `Order ${data.name}`,
      amount: data.total_price,
      pipeline: 'default',
      dealstage: 'closedwon',
    },
  }),
});

if (!response.ok) {
  throw new Error(`HubSpot API ${response.status}: ${await response.text()}`);
}
```

When creating deals, `dealstage` and `pipeline` must use HubSpot internal IDs or internal values, not the UI label text.