Primer commit
This commit is contained in:
@@ -0,0 +1,491 @@
|
||||
# GHL API — Complete Endpoint Index
|
||||
|
||||
> Generated from 22 OpenAPI JSON specification files
|
||||
> Total endpoints: 282
|
||||
|
||||
---
|
||||
|
||||
## Table of Contents
|
||||
|
||||
- [Associations (10 endpoints)](#associations)
|
||||
- [Businesses (5 endpoints)](#businesses)
|
||||
- [Calendars (41 endpoints)](#calendars)
|
||||
- [Campaigns (1 endpoints)](#campaigns)
|
||||
- [Companies (1 endpoints)](#companies)
|
||||
- [Contacts (32 endpoints)](#contacts)
|
||||
- [Conversations (29 endpoints)](#conversations)
|
||||
- [Courses (1 endpoints)](#courses)
|
||||
- [Custom Fields (8 endpoints)](#custom-fields)
|
||||
- [Emails (5 endpoints)](#emails)
|
||||
- [Forms (3 endpoints)](#forms)
|
||||
- [Funnels (7 endpoints)](#funnels)
|
||||
- [Invoices (42 endpoints)](#invoices)
|
||||
- [Links (6 endpoints)](#links)
|
||||
- [Locations (29 endpoints)](#locations)
|
||||
- [Media (7 endpoints)](#media)
|
||||
- [OAuth (3 endpoints)](#oauth)
|
||||
- [Objects (9 endpoints)](#objects)
|
||||
- [Opportunities (12 endpoints)](#opportunities)
|
||||
- [Payments (23 endpoints)](#payments)
|
||||
- [Users (7 endpoints)](#users)
|
||||
- [Workflows (1 endpoints)](#workflows)
|
||||
|
||||
---
|
||||
|
||||
## Version Header Summary
|
||||
|
||||
| Category | Version Header |
|
||||
|----------|---------------|
|
||||
| Associations | `2021-07-28` |
|
||||
| Businesses | `2021-07-28` |
|
||||
| Calendars | `2021-04-15` |
|
||||
| Campaigns | `2021-07-28` |
|
||||
| Companies | `2021-07-28` |
|
||||
| Contacts | `2021-07-28` |
|
||||
| Conversations | `2021-04-15` |
|
||||
| Courses | `2021-07-28` |
|
||||
| Custom Fields | `2021-07-28` |
|
||||
| Emails | `2021-07-28` |
|
||||
| Forms | `2021-07-28` |
|
||||
| Funnels | `2021-07-28` |
|
||||
| Invoices | `2021-07-28` |
|
||||
| Links | `2021-04-15, 2021-07-28` |
|
||||
| Locations | `2021-07-28` |
|
||||
| Media | `2021-07-28` |
|
||||
| OAuth | `2021-07-28` |
|
||||
| Objects | `2021-07-28` |
|
||||
| Opportunities | `2021-07-28` |
|
||||
| Payments | `2021-07-28` |
|
||||
| Users | `2021-07-28` |
|
||||
| Workflows | `2021-07-28` |
|
||||
|
||||
---
|
||||
|
||||
## Common API Quirks & Patterns
|
||||
|
||||
### User-Agent
|
||||
All requests to GHL API require a `User-Agent` header. Without it, you may get 403 errors.
|
||||
|
||||
### POST as GET
|
||||
Some endpoints use POST with a search body instead of GET (e.g., `/contacts/search`, `/opportunities/search`). This is because search parameters can be complex JSON.
|
||||
|
||||
### fieldValueString vs value
|
||||
When updating custom fields, use `fieldValueString` for text-based fields and `value` for numeric fields. Check the field type first via schema discovery.
|
||||
|
||||
### DELETE with body
|
||||
Some DELETE endpoints accept a request body (non-standard but supported by GHL).
|
||||
|
||||
### Pagination
|
||||
Pagination varies by endpoint. Some use `offset`/`limit`, others use cursor-based pagination with `startAfter`.
|
||||
|
||||
---
|
||||
|
||||
## Associations
|
||||
|
||||
| Method | Path | Summary | Version | Deprecated |
|
||||
|--------|------|---------|---------|------------|
|
||||
| `GET` | `/associations/` | Get all associations for a sub-account / location | `2021-07-28` | — |
|
||||
| `POST` | `/associations/` | Create Association | `2021-07-28` | — |
|
||||
| `GET` | `/associations/key/{key_name}` | Get association key by key name | `2021-07-28` | — |
|
||||
| `GET` | `/associations/objectKey/{objectKey}` | Get association by object keys | `2021-07-28` | — |
|
||||
| `POST` | `/associations/relations` | Create Relation for you associated entities. | `2021-07-28` | — |
|
||||
| `GET` | `/associations/relations/{recordId}` | Get all relations By record Id | `2021-07-28` | — |
|
||||
| `DELETE` | `/associations/relations/{relationId}` | Delete Relation | `2021-07-28` | — |
|
||||
| `DELETE` | `/associations/{associationId}` | Delete Association | `2021-07-28` | — |
|
||||
| `GET` | `/associations/{associationId}` | Get association by ID | `2021-07-28` | — |
|
||||
| `PUT` | `/associations/{associationId}` | Update Association By Id | `2021-07-28` | — |
|
||||
|
||||
## Businesses
|
||||
|
||||
| Method | Path | Summary | Version | Deprecated |
|
||||
|--------|------|---------|---------|------------|
|
||||
| `GET` | `/businesses/` | Get Businesses by Location | `2021-07-28` | — |
|
||||
| `POST` | `/businesses/` | Create Business | `2021-07-28` | — |
|
||||
| `DELETE` | `/businesses/{businessId}` | Delete Business | `2021-07-28` | — |
|
||||
| `GET` | `/businesses/{businessId}` | Get Business | `2021-07-28` | — |
|
||||
| `PUT` | `/businesses/{businessId}` | Update Business | `2021-07-28` | — |
|
||||
|
||||
## Calendars
|
||||
|
||||
> ⚠️ **Version Header:** All Calendars endpoints use `Version: 2021-04-15` (different from most other APIs which use `2021-07-28`).
|
||||
|
||||
| Method | Path | Summary | Version | Deprecated |
|
||||
|--------|------|---------|---------|------------|
|
||||
| `GET` | `/calendars/` | Get Calendars | `2021-04-15` | — |
|
||||
| `POST` | `/calendars/` | Create Calendar | `2021-04-15` | — |
|
||||
| `GET` | `/calendars/appointments/{appointmentId}/notes` | Get Notes | `2021-04-15` | — |
|
||||
| `POST` | `/calendars/appointments/{appointmentId}/notes` | Create Note | `2021-04-15` | — |
|
||||
| `DELETE` | `/calendars/appointments/{appointmentId}/notes/{noteId}` | Delete Note | `2021-04-15` | — |
|
||||
| `PUT` | `/calendars/appointments/{appointmentId}/notes/{noteId}` | Update Note | `2021-04-15` | — |
|
||||
| `GET` | `/calendars/blocked-slots` | Get Blocked Slots | `2021-04-15` | — |
|
||||
| `GET` | `/calendars/events` | Get Calendar Events | `2021-04-15` | — |
|
||||
| `POST` | `/calendars/events/appointments` | Create appointment | `2021-04-15` | — |
|
||||
| `GET` | `/calendars/events/appointments/{eventId}` | Get Appointment | `2021-04-15` | — |
|
||||
| `PUT` | `/calendars/events/appointments/{eventId}` | Update Appointment | `2021-04-15` | — |
|
||||
| `POST` | `/calendars/events/block-slots` | Create Block Slot | `2021-04-15` | — |
|
||||
| `PUT` | `/calendars/events/block-slots/{eventId}` | Update Block Slot | `2021-04-15` | — |
|
||||
| `DELETE` | `/calendars/events/{eventId}` | Delete Event | `2021-04-15` | — |
|
||||
| `GET` | `/calendars/groups` | Get Groups | `2021-04-15` | — |
|
||||
| `POST` | `/calendars/groups` | Create Calendar Group | `2021-04-15` | — |
|
||||
| `POST` | `/calendars/groups/validate-slug` | Validate group slug | `2021-04-15` | — |
|
||||
| `DELETE` | `/calendars/groups/{groupId}` | Delete Group | `2021-04-15` | — |
|
||||
| `PUT` | `/calendars/groups/{groupId}` | Update Group | `2021-04-15` | — |
|
||||
| `PUT` | `/calendars/groups/{groupId}/status` | Disable Group | `2021-04-15` | — |
|
||||
| `GET` | `/calendars/resources/{resourceType}` | List Calendar Resources | `2021-04-15` | — |
|
||||
| `POST` | `/calendars/resources/{resourceType}` | Create Calendar Resource | `2021-04-15` | — |
|
||||
| `DELETE` | `/calendars/resources/{resourceType}/{id}` | Delete Calendar Resource | `2021-04-15` | — |
|
||||
| `GET` | `/calendars/resources/{resourceType}/{id}` | Get Calendar Resource | `2021-04-15` | — |
|
||||
| `PUT` | `/calendars/resources/{resourceType}/{id}` | Update Calendar Resource | `2021-04-15` | — |
|
||||
| `POST` | `/calendars/schedules` | Create user availability schedule | `2021-04-15` | — |
|
||||
| `GET` | `/calendars/schedules/search` | List user availability schedule | `2021-04-15` | — |
|
||||
| `DELETE` | `/calendars/schedules/{id}` | Delete user availability schedule | `2021-04-15` | — |
|
||||
| `GET` | `/calendars/schedules/{id}` | Get user availability schedule | `2021-04-15` | — |
|
||||
| `PUT` | `/calendars/schedules/{id}` | Update user availability schedule | `2021-04-15` | — |
|
||||
| `DELETE` | `/calendars/schedules/{id}/associations/{calendarId}` | Remove user availability schedule from a calendar | `2021-04-15` | — |
|
||||
| `PUT` | `/calendars/schedules/{id}/associations/{calendarId}` | Apply user availability schedule to a calendar | `2021-04-15` | — |
|
||||
| `DELETE` | `/calendars/{calendarId}` | Delete Calendar | `2021-04-15` | — |
|
||||
| `GET` | `/calendars/{calendarId}` | Get Calendar | `2021-04-15` | — |
|
||||
| `PUT` | `/calendars/{calendarId}` | Update Calendar | `2021-04-15` | — |
|
||||
| `GET` | `/calendars/{calendarId}/free-slots` | Get Free Slots | `2021-04-15` | — |
|
||||
| `GET` | `/calendars/{calendarId}/notifications` | Get notifications | `2021-04-15` | — |
|
||||
| `POST` | `/calendars/{calendarId}/notifications` | Create notification | `2021-04-15` | — |
|
||||
| `DELETE` | `/calendars/{calendarId}/notifications/{notificationId}` | Delete Notification | `2021-04-15` | — |
|
||||
| `GET` | `/calendars/{calendarId}/notifications/{notificationId}` | Get notification | `2021-04-15` | — |
|
||||
| `PUT` | `/calendars/{calendarId}/notifications/{notificationId}` | Update notification | `2021-04-15` | — |
|
||||
|
||||
## Campaigns
|
||||
|
||||
| Method | Path | Summary | Version | Deprecated |
|
||||
|--------|------|---------|---------|------------|
|
||||
| `GET` | `/campaigns/` | Get Campaigns | `2021-07-28` | — |
|
||||
|
||||
## Companies
|
||||
|
||||
| Method | Path | Summary | Version | Deprecated |
|
||||
|--------|------|---------|---------|------------|
|
||||
| `GET` | `/companies/{companyId}` | Get Company | `2021-07-28` | — |
|
||||
|
||||
## Contacts
|
||||
|
||||
> ⚠️ **Deprecation:** `GET /contacts/` is deprecated. Use `POST /contacts/search` instead.
|
||||
|
||||
| Method | Path | Summary | Version | Deprecated |
|
||||
|--------|------|---------|---------|------------|
|
||||
| `GET` | `/contacts/` | Get Contacts | `2021-07-28` | ⚠️ YES |
|
||||
| `POST` | `/contacts/` | Create Contact | `2021-07-28` | — |
|
||||
| `POST` | `/contacts/bulk/business` | Add/Remove Contacts From Business | `2021-07-28` | — |
|
||||
| `POST` | `/contacts/bulk/tags/update/{type}` | Update Contacts Tags | `2021-07-28` | — |
|
||||
| `GET` | `/contacts/business/{businessId}` | Get Contacts By BusinessId | `2021-07-28` | — |
|
||||
| `POST` | `/contacts/search` | Search Contacts | `2021-07-28` | — |
|
||||
| `GET` | `/contacts/search/duplicate` | Get Duplicate Contact | `2021-07-28` | — |
|
||||
| `POST` | `/contacts/upsert` | Upsert Contact | `2021-07-28` | — |
|
||||
| `DELETE` | `/contacts/{contactId}` | Delete Contact | `2021-07-28` | — |
|
||||
| `GET` | `/contacts/{contactId}` | Get Contact | `2021-07-28` | — |
|
||||
| `PUT` | `/contacts/{contactId}` | Update Contact | `2021-07-28` | — |
|
||||
| `GET` | `/contacts/{contactId}/appointments` | Get Appointments for Contact | `2021-07-28` | — |
|
||||
| `DELETE` | `/contacts/{contactId}/campaigns/removeAll` | Remove Contact From Every Campaign | `2021-07-28` | — |
|
||||
| `DELETE` | `/contacts/{contactId}/campaigns/{campaignId}` | Remove Contact From Campaign | `2021-07-28` | — |
|
||||
| `POST` | `/contacts/{contactId}/campaigns/{campaignId}` | Add Contact to Campaign | `2021-07-28` | — |
|
||||
| `DELETE` | `/contacts/{contactId}/followers` | Remove Followers | `2021-07-28` | — |
|
||||
| `POST` | `/contacts/{contactId}/followers` | Add Followers | `2021-07-28` | — |
|
||||
| `GET` | `/contacts/{contactId}/notes` | Get All Notes | `2021-07-28` | — |
|
||||
| `POST` | `/contacts/{contactId}/notes` | Create Note | `2021-07-28` | — |
|
||||
| `DELETE` | `/contacts/{contactId}/notes/{id}` | Delete Note | `2021-07-28` | — |
|
||||
| `GET` | `/contacts/{contactId}/notes/{id}` | Get Note | `2021-07-28` | — |
|
||||
| `PUT` | `/contacts/{contactId}/notes/{id}` | Update Note | `2021-07-28` | — |
|
||||
| `DELETE` | `/contacts/{contactId}/tags` | Remove Tags | `2021-07-28` | — |
|
||||
| `POST` | `/contacts/{contactId}/tags` | Add Tags | `2021-07-28` | — |
|
||||
| `GET` | `/contacts/{contactId}/tasks` | Get all Tasks | `2021-07-28` | — |
|
||||
| `POST` | `/contacts/{contactId}/tasks` | Create Task | `2021-07-28` | — |
|
||||
| `DELETE` | `/contacts/{contactId}/tasks/{taskId}` | Delete Task | `2021-07-28` | — |
|
||||
| `GET` | `/contacts/{contactId}/tasks/{taskId}` | Get Task | `2021-07-28` | — |
|
||||
| `PUT` | `/contacts/{contactId}/tasks/{taskId}` | Update Task | `2021-07-28` | — |
|
||||
| `PUT` | `/contacts/{contactId}/tasks/{taskId}/completed` | Update Task Completed | `2021-07-28` | — |
|
||||
| `DELETE` | `/contacts/{contactId}/workflow/{workflowId}` | Delete Contact from Workflow | `2021-07-28` | — |
|
||||
| `POST` | `/contacts/{contactId}/workflow/{workflowId}` | Add Contact to Workflow | `2021-07-28` | — |
|
||||
|
||||
## Conversations
|
||||
|
||||
> ⚠️ **Version Header:** All Conversations endpoints use `Version: 2021-04-15` (different from most other APIs which use `2021-07-28`).
|
||||
|
||||
| Method | Path | Summary | Version | Deprecated |
|
||||
|--------|------|---------|---------|------------|
|
||||
| `POST` | `/conversations/` | Create Conversation | `2021-04-15` | — |
|
||||
| `GET` | `/conversations/locations/{locationId}/messages/{messageId}/transcription` | Get transcription by Message ID | `2021-04-15` | — |
|
||||
| `GET` | `/conversations/locations/{locationId}/messages/{messageId}/transcription/download` | Download transcription by Message ID | `2021-04-15` | — |
|
||||
| `POST` | `/conversations/messages` | Send a new message | `2021-04-15` | — |
|
||||
| `DELETE` | `/conversations/messages/email/{emailMessageId}/schedule` | Cancel a scheduled email message. | `—` | — |
|
||||
| `GET` | `/conversations/messages/email/{id}` | Get email by Id | `—` | — |
|
||||
| `GET` | `/conversations/messages/export` | Export messages by location ID | `2021-04-15` | — |
|
||||
| `POST` | `/conversations/messages/inbound` | Add an inbound message | `2021-04-15` | — |
|
||||
| `POST` | `/conversations/messages/outbound` | Add an external outbound call | `2021-04-15` | — |
|
||||
| `POST` | `/conversations/messages/review-reply` | Send a review reply to Google My Business | `2021-04-15` | — |
|
||||
| `POST` | `/conversations/messages/upload` | Upload file attachments | `2021-04-15` | — |
|
||||
| `POST` | `/conversations/messages/upload/complete` | Complete file upload | `2021-04-15` | — |
|
||||
| `POST` | `/conversations/messages/upload/initiate` | Initiate file upload to GCS | `2021-04-15` | — |
|
||||
| `GET` | `/conversations/messages/{id}` | Get message by message id | `2021-04-15` | — |
|
||||
| `PUT` | `/conversations/messages/{messageId}/attachments` | Add message attachments | `2021-04-15` | — |
|
||||
| `GET` | `/conversations/messages/{messageId}/locations/{locationId}/recording` | Get Recording by Message ID | `2021-04-15` | — |
|
||||
| `DELETE` | `/conversations/messages/{messageId}/schedule` | Cancel a scheduled message. | `2021-04-15` | — |
|
||||
| `PUT` | `/conversations/messages/{messageId}/status` | Update message status | `2021-04-15` | — |
|
||||
| `GET` | `/conversations/preferences/custom-subtypes` | Get All Custom Subtypes | `2021-04-15` | — |
|
||||
| `POST` | `/conversations/preferences/custom-subtypes` | Create Custom Subtype | `2021-04-15` | — |
|
||||
| `PUT` | `/conversations/preferences/custom-subtypes/{id}` | Update Custom Subtype | `2021-04-15` | — |
|
||||
| `GET` | `/conversations/preferences/unsubscriptions/status` | Get Contact Unsubscription Status | `2021-04-15` | — |
|
||||
| `POST` | `/conversations/preferences/unsubscriptions/user-change` | User Subscription Change | `2021-04-15` | — |
|
||||
| `POST` | `/conversations/providers/live-chat/typing` | Agent/Ai-Bot is typing a message indicator for live chat | `2021-04-15` | — |
|
||||
| `GET` | `/conversations/search` | Search Conversations | `2021-04-15` | — |
|
||||
| `DELETE` | `/conversations/{conversationId}` | Delete Conversation | `2021-04-15` | — |
|
||||
| `GET` | `/conversations/{conversationId}` | Get Conversation | `2021-04-15` | — |
|
||||
| `PUT` | `/conversations/{conversationId}` | Update Conversation | `2021-04-15` | — |
|
||||
| `GET` | `/conversations/{conversationId}/messages` | Get messages by conversation id | `2021-04-15` | — |
|
||||
|
||||
## Courses
|
||||
|
||||
| Method | Path | Summary | Version | Deprecated |
|
||||
|--------|------|---------|---------|------------|
|
||||
| `POST` | `/courses/courses-exporter/public/import` | Import Courses | `2021-07-28` | — |
|
||||
|
||||
## Custom Fields
|
||||
|
||||
| Method | Path | Summary | Version | Deprecated |
|
||||
|--------|------|---------|---------|------------|
|
||||
| `POST` | `/custom-fields/` | Create Custom Field | `2021-07-28` | — |
|
||||
| `POST` | `/custom-fields/folder` | Create Custom Field Folder | `2021-07-28` | — |
|
||||
| `DELETE` | `/custom-fields/folder/{id}` | Delete Custom Field Folder | `2021-07-28` | — |
|
||||
| `PUT` | `/custom-fields/folder/{id}` | Update Custom Field Folder Name | `2021-07-28` | — |
|
||||
| `GET` | `/custom-fields/object-key/{objectKey}` | Get Custom Fields By Object Key | `2021-07-28` | — |
|
||||
| `DELETE` | `/custom-fields/{id}` | Delete Custom Field By Id | `2021-07-28` | — |
|
||||
| `GET` | `/custom-fields/{id}` | Get Custom Field / Folder By Id | `2021-07-28` | — |
|
||||
| `PUT` | `/custom-fields/{id}` | Update Custom Field By Id | `2021-07-28` | — |
|
||||
|
||||
## Emails
|
||||
|
||||
| Method | Path | Summary | Version | Deprecated |
|
||||
|--------|------|---------|---------|------------|
|
||||
| `GET` | `/emails/builder` | Fetch email templates | `2021-07-28` | — |
|
||||
| `POST` | `/emails/builder` | Create a new template | `2021-07-28` | — |
|
||||
| `POST` | `/emails/builder/data` | Update a template | `2021-07-28` | — |
|
||||
| `DELETE` | `/emails/builder/{locationId}/{templateId}` | Delete a template | `2021-07-28` | — |
|
||||
| `GET` | `/emails/schedule` | Get Campaigns | `2021-07-28` | — |
|
||||
|
||||
## Forms
|
||||
|
||||
| Method | Path | Summary | Version | Deprecated |
|
||||
|--------|------|---------|---------|------------|
|
||||
| `GET` | `/forms/` | Get Forms | `2021-07-28` | — |
|
||||
| `GET` | `/forms/submissions` | Get Forms Submissions | `2021-07-28` | — |
|
||||
| `POST` | `/forms/upload-custom-files` | Upload files to custom fields | `2021-07-28` | — |
|
||||
|
||||
## Funnels
|
||||
|
||||
| Method | Path | Summary | Version | Deprecated |
|
||||
|--------|------|---------|---------|------------|
|
||||
| `GET` | `/funnels/funnel/list` | Fetch List of Funnels | `—` | — |
|
||||
| `POST` | `/funnels/lookup/redirect` | Create Redirect | `2021-07-28` | — |
|
||||
| `GET` | `/funnels/lookup/redirect/list` | Fetch List of Redirects | `2021-07-28` | — |
|
||||
| `DELETE` | `/funnels/lookup/redirect/{id}` | Delete Redirect By Id | `2021-07-28` | — |
|
||||
| `PATCH` | `/funnels/lookup/redirect/{id}` | Update Redirect By Id | `2021-07-28` | — |
|
||||
| `GET` | `/funnels/page` | Fetch list of funnel pages | `—` | — |
|
||||
| `GET` | `/funnels/page/count` | Fetch count of funnel pages | `—` | — |
|
||||
|
||||
## Invoices
|
||||
|
||||
| Method | Path | Summary | Version | Deprecated |
|
||||
|--------|------|---------|---------|------------|
|
||||
| `GET` | `/invoices/` | List invoices | `2021-07-28` | — |
|
||||
| `POST` | `/invoices/` | Create Invoice | `2021-07-28` | — |
|
||||
| `POST` | `/invoices/estimate` | Create New Estimate | `2021-07-28` | — |
|
||||
| `GET` | `/invoices/estimate/list` | List Estimates | `2021-07-28` | — |
|
||||
| `GET` | `/invoices/estimate/number/generate` | Generate Estimate Number | `2021-07-28` | — |
|
||||
| `PATCH` | `/invoices/estimate/stats/last-visited-at` | Update estimate last visited at | `2021-07-28` | — |
|
||||
| `GET` | `/invoices/estimate/template` | List Estimate Templates | `2021-07-28` | — |
|
||||
| `POST` | `/invoices/estimate/template` | Create Estimate Template | `2021-07-28` | — |
|
||||
| `GET` | `/invoices/estimate/template/preview` | Preview Estimate Template | `2021-07-28` | — |
|
||||
| `DELETE` | `/invoices/estimate/template/{templateId}` | Delete Estimate Template | `2021-07-28` | — |
|
||||
| `PUT` | `/invoices/estimate/template/{templateId}` | Update Estimate Template | `2021-07-28` | — |
|
||||
| `DELETE` | `/invoices/estimate/{estimateId}` | Delete Estimate | `2021-07-28` | — |
|
||||
| `PUT` | `/invoices/estimate/{estimateId}` | Update Estimate | `2021-07-28` | — |
|
||||
| `POST` | `/invoices/estimate/{estimateId}/invoice` | Create Invoice from Estimate | `2021-07-28` | — |
|
||||
| `POST` | `/invoices/estimate/{estimateId}/send` | Send Estimate | `2021-07-28` | — |
|
||||
| `GET` | `/invoices/generate-invoice-number` | Generate Invoice Number | `2021-07-28` | — |
|
||||
| `GET` | `/invoices/schedule` | List schedules | `2021-07-28` | — |
|
||||
| `POST` | `/invoices/schedule` | Create Invoice Schedule | `2021-07-28` | — |
|
||||
| `DELETE` | `/invoices/schedule/{scheduleId}` | Delete schedule | `2021-07-28` | — |
|
||||
| `GET` | `/invoices/schedule/{scheduleId}` | Get an schedule | `2021-07-28` | — |
|
||||
| `PUT` | `/invoices/schedule/{scheduleId}` | Update schedule | `2021-07-28` | — |
|
||||
| `POST` | `/invoices/schedule/{scheduleId}/auto-payment` | Manage Auto payment for an schedule invoice | `2021-07-28` | — |
|
||||
| `POST` | `/invoices/schedule/{scheduleId}/cancel` | Cancel an scheduled invoice | `2021-07-28` | — |
|
||||
| `POST` | `/invoices/schedule/{scheduleId}/schedule` | Schedule an schedule invoice | `2021-07-28` | — |
|
||||
| `POST` | `/invoices/schedule/{scheduleId}/updateAndSchedule` | Update scheduled recurring invoice | `2021-07-28` | — |
|
||||
| `GET` | `/invoices/settings` | Get Invoice Settings | `2021-07-28` | — |
|
||||
| `PATCH` | `/invoices/stats/last-visited-at` | Update invoice last visited at | `2021-07-28` | — |
|
||||
| `GET` | `/invoices/template` | List templates | `2021-07-28` | — |
|
||||
| `POST` | `/invoices/template` | Create template | `2021-07-28` | — |
|
||||
| `DELETE` | `/invoices/template/{templateId}` | Delete template | `2021-07-28` | — |
|
||||
| `GET` | `/invoices/template/{templateId}` | Get an template | `2021-07-28` | — |
|
||||
| `PUT` | `/invoices/template/{templateId}` | Update template | `2021-07-28` | — |
|
||||
| `PATCH` | `/invoices/template/{templateId}/late-fees-configuration` | Update template late fees configuration | `2021-07-28` | — |
|
||||
| `PATCH` | `/invoices/template/{templateId}/payment-methods-configuration` | Update template late fees configuration | `2021-07-28` | — |
|
||||
| `POST` | `/invoices/text2pay` | Create & Send | `2021-07-28` | — |
|
||||
| `DELETE` | `/invoices/{invoiceId}` | Delete invoice | `2021-07-28` | — |
|
||||
| `GET` | `/invoices/{invoiceId}` | Get invoice | `2021-07-28` | — |
|
||||
| `PUT` | `/invoices/{invoiceId}` | Update invoice | `2021-07-28` | — |
|
||||
| `PATCH` | `/invoices/{invoiceId}/late-fees-configuration` | Update invoice late fees configuration | `2021-07-28` | — |
|
||||
| `POST` | `/invoices/{invoiceId}/record-payment` | Record a manual payment for an invoice | `2021-07-28` | — |
|
||||
| `POST` | `/invoices/{invoiceId}/send` | Send invoice | `2021-07-28` | — |
|
||||
| `POST` | `/invoices/{invoiceId}/void` | Void invoice | `2021-07-28` | — |
|
||||
|
||||
## Links
|
||||
|
||||
> ⚠️ **Mixed Versions:** Most Links endpoints use `2021-07-28`, but `GET /links/search` uses `Version: 2021-04-15`.
|
||||
|
||||
| Method | Path | Summary | Version | Deprecated |
|
||||
|--------|------|---------|---------|------------|
|
||||
| `GET` | `/links/` | Get Links | `2021-07-28` | — |
|
||||
| `POST` | `/links/` | Create Link | `2021-07-28` | — |
|
||||
| `GET` | `/links/id/{linkId}` | Get Link by ID | `2021-07-28` | — |
|
||||
| `GET` | `/links/search` | Search Trigger Links | `2021-04-15` | — |
|
||||
| `DELETE` | `/links/{linkId}` | Delete Link | `2021-07-28` | — |
|
||||
| `PUT` | `/links/{linkId}` | Update Link | `2021-07-28` | — |
|
||||
|
||||
## Locations
|
||||
|
||||
| Method | Path | Summary | Version | Deprecated |
|
||||
|--------|------|---------|---------|------------|
|
||||
| `POST` | `/locations/` | Create Sub-Account (Formerly Location) | `2021-07-28` | — |
|
||||
| `GET` | `/locations/search` | Search | `2021-07-28` | — |
|
||||
| `DELETE` | `/locations/{locationId}` | Delete Sub-Account (Formerly Location) | `2021-07-28` | — |
|
||||
| `GET` | `/locations/{locationId}` | Get Sub-Account (Formerly Location) | `2021-07-28` | — |
|
||||
| `PUT` | `/locations/{locationId}` | Put Sub-Account (Formerly Location) | `2021-07-28` | — |
|
||||
| `GET` | `/locations/{locationId}/customFields` | Get Custom Fields | `2021-07-28` | — |
|
||||
| `POST` | `/locations/{locationId}/customFields` | Create Custom Field | `2021-07-28` | — |
|
||||
| `POST` | `/locations/{locationId}/customFields/upload` | Uploads File to customFields | `2021-07-28` | — |
|
||||
| `DELETE` | `/locations/{locationId}/customFields/{id}` | Delete Custom Field | `2021-07-28` | — |
|
||||
| `GET` | `/locations/{locationId}/customFields/{id}` | Get Custom Field | `2021-07-28` | — |
|
||||
| `PUT` | `/locations/{locationId}/customFields/{id}` | Update Custom Field | `2021-07-28` | — |
|
||||
| `GET` | `/locations/{locationId}/customValues` | Get Custom Values | `2021-07-28` | — |
|
||||
| `POST` | `/locations/{locationId}/customValues` | Create Custom Value | `2021-07-28` | — |
|
||||
| `DELETE` | `/locations/{locationId}/customValues/{id}` | Delete Custom Value | `2021-07-28` | — |
|
||||
| `GET` | `/locations/{locationId}/customValues/{id}` | Get Custom Value | `2021-07-28` | — |
|
||||
| `PUT` | `/locations/{locationId}/customValues/{id}` | Update Custom Value | `2021-07-28` | — |
|
||||
| `POST` | `/locations/{locationId}/recurring-tasks` | Create Recurring Task | `2021-07-28` | — |
|
||||
| `DELETE` | `/locations/{locationId}/recurring-tasks/{id}` | Delete Recurring Task | `2021-07-28` | — |
|
||||
| `GET` | `/locations/{locationId}/recurring-tasks/{id}` | Get Recurring Task By Id | `2021-07-28` | — |
|
||||
| `PUT` | `/locations/{locationId}/recurring-tasks/{id}` | Update Recurring Task | `2021-07-28` | — |
|
||||
| `GET` | `/locations/{locationId}/tags` | Get Tags | `2021-07-28` | — |
|
||||
| `POST` | `/locations/{locationId}/tags` | Create Tag | `2021-07-28` | — |
|
||||
| `DELETE` | `/locations/{locationId}/tags/{tagId}` | Delete tag | `2021-07-28` | — |
|
||||
| `GET` | `/locations/{locationId}/tags/{tagId}` | Get tag by id | `2021-07-28` | — |
|
||||
| `PUT` | `/locations/{locationId}/tags/{tagId}` | Update tag | `2021-07-28` | — |
|
||||
| `POST` | `/locations/{locationId}/tasks/search` | Task Search Filter | `2021-07-28` | — |
|
||||
| `GET` | `/locations/{locationId}/templates` | GET all or email/sms templates | `2021-07-28` | — |
|
||||
| `DELETE` | `/locations/{locationId}/templates/{id}` | DELETE an email/sms template | `2021-07-28` | — |
|
||||
| `GET` | `/locations/{locationId}/timezones` | Fetch Timezones | `2021-07-28` | — |
|
||||
|
||||
## Media
|
||||
|
||||
| Method | Path | Summary | Version | Deprecated |
|
||||
|--------|------|---------|---------|------------|
|
||||
| `PUT` | `/medias/delete-files` | Bulk Delete / Trash Files or Folders | `2021-07-28` | — |
|
||||
| `GET` | `/medias/files` | Get List of Files/ Folders | `2021-07-28` | — |
|
||||
| `POST` | `/medias/folder` | Create Folder | `2021-07-28` | — |
|
||||
| `PUT` | `/medias/update-files` | Bulk Update Files/ Folders | `2021-07-28` | — |
|
||||
| `POST` | `/medias/upload-file` | Upload File into Media Storage | `2021-07-28` | — |
|
||||
| `DELETE` | `/medias/{id}` | Delete File or Folder | `2021-07-28` | — |
|
||||
| `POST` | `/medias/{id}` | Update File/ Folder | `2021-07-28` | — |
|
||||
|
||||
## OAuth
|
||||
|
||||
| Method | Path | Summary | Version | Deprecated |
|
||||
|--------|------|---------|---------|------------|
|
||||
| `GET` | `/oauth/installedLocations` | Get Location where app is installed | `2021-07-28` | — |
|
||||
| `POST` | `/oauth/locationToken` | Get Location Access Token from Agency Token | `2021-07-28` | — |
|
||||
| `POST` | `/oauth/token` | Get Access Token | `—` | — |
|
||||
|
||||
## Objects
|
||||
|
||||
> ⚠️ **Schema Discovery:** Use `GET /objects/{key}?locationId=LOC_ID` with `Version: 2021-07-28` for schema discovery. Object key for custom objects must include `custom_objects.` prefix.
|
||||
|
||||
| Method | Path | Summary | Version | Deprecated |
|
||||
|--------|------|---------|---------|------------|
|
||||
| `GET` | `/objects/` | Get all objects for a location | `2021-07-28` | — |
|
||||
| `POST` | `/objects/` | Create Custom Object | `2021-07-28` | — |
|
||||
| `GET` | `/objects/{key}` | Get Object Schema by key / id | `2021-07-28` | — |
|
||||
| `PUT` | `/objects/{key}` | Update Object Schema By Key / Id | `2021-07-28` | — |
|
||||
| `POST` | `/objects/{schemaKey}/records` | Create Record | `2021-07-28` | — |
|
||||
| `POST` | `/objects/{schemaKey}/records/search` | Search Object Records | `2021-07-28` | — |
|
||||
| `DELETE` | `/objects/{schemaKey}/records/{id}` | Delete Record | `2021-07-28` | — |
|
||||
| `GET` | `/objects/{schemaKey}/records/{id}` | Get Record By Id | `2021-07-28` | — |
|
||||
| `PUT` | `/objects/{schemaKey}/records/{id}` | Update Record | `2021-07-28` | — |
|
||||
|
||||
## Opportunities
|
||||
|
||||
> ⚠️ **Search Quirk:** The `POST /opportunities/search` endpoint sometimes has issues filtering by `contactId` directly. Use the newer search endpoint or work around with custom filters.
|
||||
|
||||
| Method | Path | Summary | Version | Deprecated |
|
||||
|--------|------|---------|---------|------------|
|
||||
| `POST` | `/opportunities/` | Create Opportunity | `2021-07-28` | — |
|
||||
| `GET` | `/opportunities/lost-reason` | Get lost reason | `2021-07-28` | — |
|
||||
| `GET` | `/opportunities/pipelines` | Get Pipelines | `2021-07-28` | — |
|
||||
| `GET` | `/opportunities/search` | Search Opportunity | `2021-07-28` | — |
|
||||
| `POST` | `/opportunities/search` | Search Opportunities | `2021-07-28` | — |
|
||||
| `POST` | `/opportunities/upsert` | Upsert Opportunity | `2021-07-28` | — |
|
||||
| `DELETE` | `/opportunities/{id}` | Delete Opportunity | `2021-07-28` | — |
|
||||
| `GET` | `/opportunities/{id}` | Get Opportunity | `2021-07-28` | — |
|
||||
| `PUT` | `/opportunities/{id}` | Update Opportunity | `2021-07-28` | — |
|
||||
| `DELETE` | `/opportunities/{id}/followers` | Remove Followers | `2021-07-28` | — |
|
||||
| `POST` | `/opportunities/{id}/followers` | Add Followers | `2021-07-28` | — |
|
||||
| `PUT` | `/opportunities/{id}/status` | Update Opportunity Status | `2021-07-28` | — |
|
||||
|
||||
## Payments
|
||||
|
||||
| Method | Path | Summary | Version | Deprecated |
|
||||
|--------|------|---------|---------|------------|
|
||||
| `DELETE` | `/payments/coupon` | Delete Coupon | `2021-07-28` | — |
|
||||
| `GET` | `/payments/coupon` | Fetch Coupon | `2021-07-28` | — |
|
||||
| `POST` | `/payments/coupon` | Create Coupon | `2021-07-28` | — |
|
||||
| `PUT` | `/payments/coupon` | Update Coupon | `2021-07-28` | — |
|
||||
| `GET` | `/payments/coupon/list` | List Coupons | `2021-07-28` | — |
|
||||
| `PUT` | `/payments/custom-provider/capabilities` | Custom-provider marketplace app update capabilities | `2021-07-28` | — |
|
||||
| `GET` | `/payments/custom-provider/connect` | Fetch given provider config | `2021-07-28` | — |
|
||||
| `POST` | `/payments/custom-provider/connect` | Create new provider config | `2021-07-28` | — |
|
||||
| `POST` | `/payments/custom-provider/disconnect` | Disconnect existing provider config | `2021-07-28` | — |
|
||||
| `DELETE` | `/payments/custom-provider/provider` | Deleting an existing integration | `2021-07-28` | — |
|
||||
| `POST` | `/payments/custom-provider/provider` | Create new integration | `2021-07-28` | — |
|
||||
| `GET` | `/payments/integrations/provider/whitelabel` | List White-label Integration Providers | `2021-07-28` | — |
|
||||
| `POST` | `/payments/integrations/provider/whitelabel` | Create White-label Integration Provider | `2021-07-28` | — |
|
||||
| `GET` | `/payments/orders` | List Orders | `2021-07-28` | — |
|
||||
| `GET` | `/payments/orders/{orderId}` | Get Order by ID | `2021-07-28` | — |
|
||||
| `GET` | `/payments/orders/{orderId}/fulfillments` | List fulfillment | `2021-07-28` | — |
|
||||
| `POST` | `/payments/orders/{orderId}/fulfillments` | Create order fulfillment | `2021-07-28` | — |
|
||||
| `GET` | `/payments/orders/{orderId}/notes` | List Order Notes | `2021-07-28` | — |
|
||||
| `POST` | `/payments/orders/{orderId}/record-payment` | Record Order Payment | `2021-07-28` | — |
|
||||
| `GET` | `/payments/subscriptions` | List Subscriptions | `2021-07-28` | — |
|
||||
| `GET` | `/payments/subscriptions/{subscriptionId}` | Get Subscription by ID | `2021-07-28` | — |
|
||||
| `GET` | `/payments/transactions` | List Transactions | `2021-07-28` | — |
|
||||
| `GET` | `/payments/transactions/{transactionId}` | Get Transaction by ID | `2021-07-28` | — |
|
||||
|
||||
## Users
|
||||
|
||||
> ⚠️ **Deprecations:** Email update is deprecated. User deletion is asynchronous (returns 202 Accepted). `GET /users/` is deprecated — use `GET /users/search` instead.
|
||||
|
||||
| Method | Path | Summary | Version | Deprecated |
|
||||
|--------|------|---------|---------|------------|
|
||||
| `GET` | `/users/` | Get User by Location | `2021-07-28` | ⚠️ YES |
|
||||
| `POST` | `/users/` | Create User | `2021-07-28` | — |
|
||||
| `GET` | `/users/search` | Search Users | `2021-07-28` | — |
|
||||
| `POST` | `/users/search/filter-by-email` | Filter Users by Email | `2021-07-28` | — |
|
||||
| `DELETE` | `/users/{userId}` | Delete User | `2021-07-28` | — |
|
||||
| `GET` | `/users/{userId}` | Get User | `2021-07-28` | — |
|
||||
| `PUT` | `/users/{userId}` | Update User | `2021-07-28` | — |
|
||||
|
||||
## Workflows
|
||||
|
||||
| Method | Path | Summary | Version | Deprecated |
|
||||
|--------|------|---------|---------|------------|
|
||||
| `GET` | `/workflows/` | Get Workflow | `2021-07-28` | — |
|
||||
|
||||
---
|
||||
|
||||
*Documentation generated from GHL OpenAPI specifications. Always verify against the official GHL API documentation for the latest changes.*
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,659 @@
|
||||
# GHL Businesses API
|
||||
|
||||
**Endpoints:** 5
|
||||
**Base URL:** `https://rest.gohighlevel.com/v1`
|
||||
|
||||
**Version Header(s):** `2021-07-28`
|
||||
|
||||
---
|
||||
|
||||
## Endpoints
|
||||
|
||||
- [GET /businesses/](#get--businesses-) — Get Businesses by Location
|
||||
- [POST /businesses/](#post--businesses-) — Create Business
|
||||
- [DELETE /businesses/{businessId}](#delete--businesses-businessId) — Delete Business
|
||||
- [GET /businesses/{businessId}](#get--businesses-businessId) — Get Business
|
||||
- [PUT /businesses/{businessId}](#put--businesses-businessId) — Update Business
|
||||
|
||||
---
|
||||
|
||||
## GET /businesses/
|
||||
|
||||
**Summary:** Get Businesses by Location
|
||||
|
||||
Get Businesses by Location
|
||||
|
||||
**Version Header:** `2021-07-28`
|
||||
|
||||
**Operation ID:** `get-businesses-by-location`
|
||||
|
||||
**Tags:** Businesses
|
||||
|
||||
### cURL
|
||||
|
||||
```bash
|
||||
curl -X GET 'https://rest.gohighlevel.com/v1/businesses/?locationId=5DP4iH6HLkQsiKESj6rh&limit=100&skip=10' \
|
||||
-H 'Authorization: Bearer YOUR_API_TOKEN' \
|
||||
-H 'Version: 2021-07-28' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-H 'Accept: application/json' \
|
||||
-H 'User-Agent: YourApp/1.0'
|
||||
```
|
||||
|
||||
### Python (http.client)
|
||||
|
||||
```python
|
||||
import http.client
|
||||
import json
|
||||
|
||||
conn = http.client.HTTPSConnection('rest.gohighlevel.com')
|
||||
|
||||
headers = {
|
||||
'Authorization': 'Bearer YOUR_API_TOKEN',
|
||||
'Version': '2021-07-28',
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json',
|
||||
'User-Agent': 'YourApp/1.0',
|
||||
}
|
||||
|
||||
conn.request('GET', '/v1/businesses/?locationId=5DP4iH6HLkQsiKESj6rh&limit=100&skip=10', headers=headers)
|
||||
|
||||
response = conn.getresponse()
|
||||
data = json.loads(response.read().decode())
|
||||
print(json.dumps(data, indent=2))
|
||||
conn.close()
|
||||
```
|
||||
|
||||
### n8n HTTP Node
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "GHL - GET businesses",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"parameters": {
|
||||
"method": "GET",
|
||||
"url": "https://rest.gohighlevel.com/v1/businesses/",
|
||||
"authentication": "genericCredentialType",
|
||||
"genericAuthType": "httpHeaderAuth",
|
||||
"sendHeaders": true,
|
||||
"headerParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "Authorization",
|
||||
"value": "Bearer YOUR_API_TOKEN"
|
||||
},
|
||||
{
|
||||
"name": "Version",
|
||||
"value": "2021-07-28"
|
||||
},
|
||||
{
|
||||
"name": "Content-Type",
|
||||
"value": "application/json"
|
||||
},
|
||||
{
|
||||
"name": "Accept",
|
||||
"value": "application/json"
|
||||
},
|
||||
{
|
||||
"name": "User-Agent",
|
||||
"value": "YourApp/1.0"
|
||||
}
|
||||
]
|
||||
},
|
||||
"sendBody": false,
|
||||
"bodyParameters": {
|
||||
"parameters": []
|
||||
},
|
||||
"options": {},
|
||||
"sendQuery": true,
|
||||
"queryParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "locationId",
|
||||
"value": "5DP4iH6HLkQsiKESj6rh"
|
||||
},
|
||||
{
|
||||
"name": "limit",
|
||||
"value": "100"
|
||||
},
|
||||
{
|
||||
"name": "skip",
|
||||
"value": "10"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**To use in n8n:**
|
||||
1. Add an HTTP Request node
|
||||
2. Switch to JSON mode (Parameters → use RAW JSON)
|
||||
3. Paste the JSON above
|
||||
4. Update `YOUR_API_TOKEN` with your actual GHL API key
|
||||
|
||||
### Request Parameters
|
||||
|
||||
| Name | Location | Type | Required | Description |
|
||||
|------|----------|------|----------|-------------|
|
||||
| `locationId` | Query | `string` | ✅ | [example: `5DP4iH6HLkQsiKESj6rh`] |
|
||||
| `limit` | Query | `string` | — | [example: `100`] |
|
||||
| `skip` | Query | `string` | — | [example: `10`] |
|
||||
|
||||
### Response Codes
|
||||
|
||||
| Code | Description | Schema |
|
||||
|------|-------------|--------|
|
||||
| `200` | Successful response | - **businesses** (array of BusinessDto) (required): Business Response |
|
||||
| `400` | Bad Request | Standard error response for bad requests (400). Contains message, statusCode fields. |
|
||||
| `401` | Unauthorized | Standard error response for unauthorized requests (401). Contains message, statusCode fields. |
|
||||
|
||||
### ⚠️ Special Notes & Quirks
|
||||
|
||||
> ⚠️ **User-Agent Required:** All GHL API requests require a `User-Agent` HTTP header. Requests without it may be rejected with a 403 error.
|
||||
|
||||
---
|
||||
|
||||
## POST /businesses/
|
||||
|
||||
**Summary:** Create Business
|
||||
|
||||
Create Business
|
||||
|
||||
**Version Header:** `2021-07-28`
|
||||
|
||||
**Operation ID:** `create-business`
|
||||
|
||||
**Tags:** Businesses
|
||||
|
||||
### cURL
|
||||
|
||||
```bash
|
||||
curl -X POST 'https://rest.gohighlevel.com/v1/businesses/' \
|
||||
-H 'Authorization: Bearer YOUR_API_TOKEN' \
|
||||
-H 'Version: 2021-07-28' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-H 'Accept: application/json' \
|
||||
-H 'User-Agent: YourApp/1.0' \
|
||||
-d '{}' # See request body schema below
|
||||
```
|
||||
|
||||
### Python (http.client)
|
||||
|
||||
```python
|
||||
import http.client
|
||||
import json
|
||||
|
||||
conn = http.client.HTTPSConnection('rest.gohighlevel.com')
|
||||
|
||||
headers = {
|
||||
'Authorization': 'Bearer YOUR_API_TOKEN',
|
||||
'Version': '2021-07-28',
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json',
|
||||
'User-Agent': 'YourApp/1.0',
|
||||
}
|
||||
|
||||
payload = json.dumps({})
|
||||
conn.request('POST', '/v1/businesses/', body=payload, headers=headers)
|
||||
|
||||
response = conn.getresponse()
|
||||
data = json.loads(response.read().decode())
|
||||
print(json.dumps(data, indent=2))
|
||||
conn.close()
|
||||
```
|
||||
|
||||
### n8n HTTP Node
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "GHL - POST businesses",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"parameters": {
|
||||
"method": "POST",
|
||||
"url": "https://rest.gohighlevel.com/v1/businesses/",
|
||||
"authentication": "genericCredentialType",
|
||||
"genericAuthType": "httpHeaderAuth",
|
||||
"sendHeaders": true,
|
||||
"headerParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "Authorization",
|
||||
"value": "Bearer YOUR_API_TOKEN"
|
||||
},
|
||||
{
|
||||
"name": "Version",
|
||||
"value": "2021-07-28"
|
||||
},
|
||||
{
|
||||
"name": "Content-Type",
|
||||
"value": "application/json"
|
||||
},
|
||||
{
|
||||
"name": "Accept",
|
||||
"value": "application/json"
|
||||
},
|
||||
{
|
||||
"name": "User-Agent",
|
||||
"value": "YourApp/1.0"
|
||||
}
|
||||
]
|
||||
},
|
||||
"sendBody": true,
|
||||
"bodyParameters": {
|
||||
"parameters": []
|
||||
},
|
||||
"options": {}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**To use in n8n:**
|
||||
1. Add an HTTP Request node
|
||||
2. Switch to JSON mode (Parameters → use RAW JSON)
|
||||
3. Paste the JSON above
|
||||
4. Update `YOUR_API_TOKEN` with your actual GHL API key
|
||||
|
||||
### Request Body Schema
|
||||
|
||||
- **name** (string) (required) (e.g. `Microsoft`)
|
||||
- **locationId** (string) (required) (e.g. `5DP4iH6HLkQsiKESj6rh`)
|
||||
- **phone** (string) (e.g. `+18832327657`)
|
||||
- **email** (string) (e.g. `john@deo.com`)
|
||||
- **website** (string) (e.g. `www.xyz.com`)
|
||||
- **address** (string) (e.g. `street adress`)
|
||||
- **city** (string) (e.g. `new york`)
|
||||
- **postalCode** (string) (e.g. `12312312`)
|
||||
- **state** (string) (e.g. `new york`)
|
||||
- **country** (string) (e.g. `us`)
|
||||
- **description** (string) (e.g. `business description`)
|
||||
|
||||
### Response Codes
|
||||
|
||||
| Code | Description | Schema |
|
||||
|------|-------------|--------|
|
||||
| `201` | Successful response | - **success** (boolean) (required) (e.g. `True`): Success Value<br> - **buiseness** (string) (required): Business Response |
|
||||
| `400` | Bad Request | Standard error response for bad requests (400). Contains message, statusCode fields. |
|
||||
| `401` | Unauthorized | Standard error response for unauthorized requests (401). Contains message, statusCode fields. |
|
||||
| `422` | Unprocessable Entity | Standard error response for unprocessable entity (422). Contains message, statusCode, errors array fields. |
|
||||
|
||||
### ⚠️ Special Notes & Quirks
|
||||
|
||||
> ⚠️ **User-Agent Required:** All GHL API requests require a `User-Agent` HTTP header. Requests without it may be rejected with a 403 error.
|
||||
|
||||
---
|
||||
|
||||
## DELETE /businesses/{businessId}
|
||||
|
||||
**Summary:** Delete Business
|
||||
|
||||
Delete Business
|
||||
|
||||
**Version Header:** `2021-07-28`
|
||||
|
||||
**Operation ID:** `delete-Business`
|
||||
|
||||
**Tags:** Businesses
|
||||
|
||||
### cURL
|
||||
|
||||
```bash
|
||||
curl -X DELETE 'https://rest.gohighlevel.com/v1/businesses/VALUE' \
|
||||
-H 'Authorization: Bearer YOUR_API_TOKEN' \
|
||||
-H 'Version: 2021-07-28' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-H 'Accept: application/json' \
|
||||
-H 'User-Agent: YourApp/1.0'
|
||||
```
|
||||
|
||||
### Python (http.client)
|
||||
|
||||
```python
|
||||
import http.client
|
||||
import json
|
||||
|
||||
conn = http.client.HTTPSConnection('rest.gohighlevel.com')
|
||||
|
||||
headers = {
|
||||
'Authorization': 'Bearer YOUR_API_TOKEN',
|
||||
'Version': '2021-07-28',
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json',
|
||||
'User-Agent': 'YourApp/1.0',
|
||||
}
|
||||
|
||||
conn.request('DELETE', '/v1/businesses/VALUE', headers=headers)
|
||||
|
||||
response = conn.getresponse()
|
||||
data = json.loads(response.read().decode())
|
||||
print(json.dumps(data, indent=2))
|
||||
conn.close()
|
||||
```
|
||||
|
||||
### n8n HTTP Node
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "GHL - DELETE VALUE",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"parameters": {
|
||||
"method": "DELETE",
|
||||
"url": "https://rest.gohighlevel.com/v1/businesses/VALUE",
|
||||
"authentication": "genericCredentialType",
|
||||
"genericAuthType": "httpHeaderAuth",
|
||||
"sendHeaders": true,
|
||||
"headerParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "Authorization",
|
||||
"value": "Bearer YOUR_API_TOKEN"
|
||||
},
|
||||
{
|
||||
"name": "Version",
|
||||
"value": "2021-07-28"
|
||||
},
|
||||
{
|
||||
"name": "Content-Type",
|
||||
"value": "application/json"
|
||||
},
|
||||
{
|
||||
"name": "Accept",
|
||||
"value": "application/json"
|
||||
},
|
||||
{
|
||||
"name": "User-Agent",
|
||||
"value": "YourApp/1.0"
|
||||
}
|
||||
]
|
||||
},
|
||||
"sendBody": false,
|
||||
"bodyParameters": {
|
||||
"parameters": []
|
||||
},
|
||||
"options": {}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**To use in n8n:**
|
||||
1. Add an HTTP Request node
|
||||
2. Switch to JSON mode (Parameters → use RAW JSON)
|
||||
3. Paste the JSON above
|
||||
4. Update `YOUR_API_TOKEN` with your actual GHL API key
|
||||
|
||||
### Request Parameters
|
||||
|
||||
| Name | Location | Type | Required | Description |
|
||||
|------|----------|------|----------|-------------|
|
||||
| `businessId` | Path | `string` | ✅ | |
|
||||
|
||||
### Response Codes
|
||||
|
||||
| Code | Description | Schema |
|
||||
|------|-------------|--------|
|
||||
| `200` | Successful response | - **success** (boolean) (required) (e.g. `True`): Success value |
|
||||
| `400` | Bad Request | Standard error response for bad requests (400). Contains message, statusCode fields. |
|
||||
| `401` | Unauthorized | Standard error response for unauthorized requests (401). Contains message, statusCode fields. |
|
||||
| `422` | Unprocessable Entity | Standard error response for unprocessable entity (422). Contains message, statusCode, errors array fields. |
|
||||
|
||||
### ⚠️ Special Notes & Quirks
|
||||
|
||||
> ⚠️ **User-Agent Required:** All GHL API requests require a `User-Agent` HTTP header. Requests without it may be rejected with a 403 error.
|
||||
|
||||
---
|
||||
|
||||
## GET /businesses/{businessId}
|
||||
|
||||
**Summary:** Get Business
|
||||
|
||||
Get Business
|
||||
|
||||
**Version Header:** `2021-07-28`
|
||||
|
||||
**Operation ID:** `get-business`
|
||||
|
||||
**Tags:** Businesses
|
||||
|
||||
### cURL
|
||||
|
||||
```bash
|
||||
curl -X GET 'https://rest.gohighlevel.com/v1/businesses/VALUE' \
|
||||
-H 'Authorization: Bearer YOUR_API_TOKEN' \
|
||||
-H 'Version: 2021-07-28' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-H 'Accept: application/json' \
|
||||
-H 'User-Agent: YourApp/1.0'
|
||||
```
|
||||
|
||||
### Python (http.client)
|
||||
|
||||
```python
|
||||
import http.client
|
||||
import json
|
||||
|
||||
conn = http.client.HTTPSConnection('rest.gohighlevel.com')
|
||||
|
||||
headers = {
|
||||
'Authorization': 'Bearer YOUR_API_TOKEN',
|
||||
'Version': '2021-07-28',
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json',
|
||||
'User-Agent': 'YourApp/1.0',
|
||||
}
|
||||
|
||||
conn.request('GET', '/v1/businesses/VALUE', headers=headers)
|
||||
|
||||
response = conn.getresponse()
|
||||
data = json.loads(response.read().decode())
|
||||
print(json.dumps(data, indent=2))
|
||||
conn.close()
|
||||
```
|
||||
|
||||
### n8n HTTP Node
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "GHL - GET VALUE",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"parameters": {
|
||||
"method": "GET",
|
||||
"url": "https://rest.gohighlevel.com/v1/businesses/VALUE",
|
||||
"authentication": "genericCredentialType",
|
||||
"genericAuthType": "httpHeaderAuth",
|
||||
"sendHeaders": true,
|
||||
"headerParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "Authorization",
|
||||
"value": "Bearer YOUR_API_TOKEN"
|
||||
},
|
||||
{
|
||||
"name": "Version",
|
||||
"value": "2021-07-28"
|
||||
},
|
||||
{
|
||||
"name": "Content-Type",
|
||||
"value": "application/json"
|
||||
},
|
||||
{
|
||||
"name": "Accept",
|
||||
"value": "application/json"
|
||||
},
|
||||
{
|
||||
"name": "User-Agent",
|
||||
"value": "YourApp/1.0"
|
||||
}
|
||||
]
|
||||
},
|
||||
"sendBody": false,
|
||||
"bodyParameters": {
|
||||
"parameters": []
|
||||
},
|
||||
"options": {}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**To use in n8n:**
|
||||
1. Add an HTTP Request node
|
||||
2. Switch to JSON mode (Parameters → use RAW JSON)
|
||||
3. Paste the JSON above
|
||||
4. Update `YOUR_API_TOKEN` with your actual GHL API key
|
||||
|
||||
### Request Parameters
|
||||
|
||||
| Name | Location | Type | Required | Description |
|
||||
|------|----------|------|----------|-------------|
|
||||
| `businessId` | Path | `string` | ✅ | |
|
||||
|
||||
### Response Codes
|
||||
|
||||
| Code | Description | Schema |
|
||||
|------|-------------|--------|
|
||||
| `200` | Successful response | - **business** (string) (required): Business Response |
|
||||
| `400` | Bad Request | Standard error response for bad requests (400). Contains message, statusCode fields. |
|
||||
| `401` | Unauthorized | Standard error response for unauthorized requests (401). Contains message, statusCode fields. |
|
||||
| `422` | Unprocessable Entity | Standard error response for unprocessable entity (422). Contains message, statusCode, errors array fields. |
|
||||
|
||||
### ⚠️ Special Notes & Quirks
|
||||
|
||||
> ⚠️ **User-Agent Required:** All GHL API requests require a `User-Agent` HTTP header. Requests without it may be rejected with a 403 error.
|
||||
|
||||
---
|
||||
|
||||
## PUT /businesses/{businessId}
|
||||
|
||||
**Summary:** Update Business
|
||||
|
||||
Update Business
|
||||
|
||||
**Version Header:** `2021-07-28`
|
||||
|
||||
**Operation ID:** `update-business`
|
||||
|
||||
**Tags:** Businesses
|
||||
|
||||
### cURL
|
||||
|
||||
```bash
|
||||
curl -X PUT 'https://rest.gohighlevel.com/v1/businesses/VALUE' \
|
||||
-H 'Authorization: Bearer YOUR_API_TOKEN' \
|
||||
-H 'Version: 2021-07-28' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-H 'Accept: application/json' \
|
||||
-H 'User-Agent: YourApp/1.0' \
|
||||
-d '{}' # See request body schema below
|
||||
```
|
||||
|
||||
### Python (http.client)
|
||||
|
||||
```python
|
||||
import http.client
|
||||
import json
|
||||
|
||||
conn = http.client.HTTPSConnection('rest.gohighlevel.com')
|
||||
|
||||
headers = {
|
||||
'Authorization': 'Bearer YOUR_API_TOKEN',
|
||||
'Version': '2021-07-28',
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json',
|
||||
'User-Agent': 'YourApp/1.0',
|
||||
}
|
||||
|
||||
payload = json.dumps({})
|
||||
conn.request('PUT', '/v1/businesses/VALUE', body=payload, headers=headers)
|
||||
|
||||
response = conn.getresponse()
|
||||
data = json.loads(response.read().decode())
|
||||
print(json.dumps(data, indent=2))
|
||||
conn.close()
|
||||
```
|
||||
|
||||
### n8n HTTP Node
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "GHL - PUT VALUE",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"parameters": {
|
||||
"method": "PUT",
|
||||
"url": "https://rest.gohighlevel.com/v1/businesses/VALUE",
|
||||
"authentication": "genericCredentialType",
|
||||
"genericAuthType": "httpHeaderAuth",
|
||||
"sendHeaders": true,
|
||||
"headerParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "Authorization",
|
||||
"value": "Bearer YOUR_API_TOKEN"
|
||||
},
|
||||
{
|
||||
"name": "Version",
|
||||
"value": "2021-07-28"
|
||||
},
|
||||
{
|
||||
"name": "Content-Type",
|
||||
"value": "application/json"
|
||||
},
|
||||
{
|
||||
"name": "Accept",
|
||||
"value": "application/json"
|
||||
},
|
||||
{
|
||||
"name": "User-Agent",
|
||||
"value": "YourApp/1.0"
|
||||
}
|
||||
]
|
||||
},
|
||||
"sendBody": true,
|
||||
"bodyParameters": {
|
||||
"parameters": []
|
||||
},
|
||||
"options": {}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**To use in n8n:**
|
||||
1. Add an HTTP Request node
|
||||
2. Switch to JSON mode (Parameters → use RAW JSON)
|
||||
3. Paste the JSON above
|
||||
4. Update `YOUR_API_TOKEN` with your actual GHL API key
|
||||
|
||||
### Request Parameters
|
||||
|
||||
| Name | Location | Type | Required | Description |
|
||||
|------|----------|------|----------|-------------|
|
||||
| `businessId` | Path | `string` | ✅ | |
|
||||
|
||||
### Request Body Schema
|
||||
|
||||
- **name** (string) (e.g. `Microsoft`)
|
||||
- **phone** (string) (e.g. `+18832327657`)
|
||||
- **email** (string) (e.g. `john@deo.com`)
|
||||
- **postalCode** (string) (e.g. `12312312`)
|
||||
- **website** (string) (e.g. `www.xyz.com`)
|
||||
- **address** (string) (e.g. `street adress`)
|
||||
- **state** (string) (e.g. `new york`)
|
||||
- **city** (string) (e.g. `new york`)
|
||||
- **country** (string) (e.g. `us`)
|
||||
- **description** (string) (e.g. `business description`)
|
||||
|
||||
### Response Codes
|
||||
|
||||
| Code | Description | Schema |
|
||||
|------|-------------|--------|
|
||||
| `200` | Successful response | - **success** (boolean) (required) (e.g. `True`): Success Value<br> - **buiseness** (string) (required): Business Response |
|
||||
| `400` | Bad Request | Standard error response for bad requests (400). Contains message, statusCode fields. |
|
||||
| `401` | Unauthorized | Standard error response for unauthorized requests (401). Contains message, statusCode fields. |
|
||||
| `422` | Unprocessable Entity | Standard error response for unprocessable entity (422). Contains message, statusCode, errors array fields. |
|
||||
|
||||
### ⚠️ Special Notes & Quirks
|
||||
|
||||
> ⚠️ **User-Agent Required:** All GHL API requests require a `User-Agent` HTTP header. Requests without it may be rejected with a 403 error.
|
||||
|
||||
---
|
||||
|
||||
|
||||
*Documentation generated from GHL OpenAPI specifications. Verify against official docs for latest changes.*
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,150 @@
|
||||
# GHL Campaigns API
|
||||
|
||||
**Endpoints:** 1
|
||||
**Base URL:** `https://rest.gohighlevel.com/v1`
|
||||
|
||||
**Version Header(s):** `2021-07-28`
|
||||
|
||||
---
|
||||
|
||||
## Endpoints
|
||||
|
||||
- [GET /campaigns/](#get--campaigns-) — Get Campaigns
|
||||
|
||||
---
|
||||
|
||||
## GET /campaigns/
|
||||
|
||||
**Summary:** Get Campaigns
|
||||
|
||||
Get Campaigns
|
||||
|
||||
**Version Header:** `2021-07-28`
|
||||
|
||||
**Operation ID:** `get-campaigns`
|
||||
|
||||
**Tags:** Campaigns
|
||||
|
||||
### cURL
|
||||
|
||||
```bash
|
||||
curl -X GET 'https://rest.gohighlevel.com/v1/campaigns/?locationId=VALUE&status=VALUE' \
|
||||
-H 'Authorization: Bearer YOUR_API_TOKEN' \
|
||||
-H 'Version: 2021-07-28' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-H 'Accept: application/json' \
|
||||
-H 'User-Agent: YourApp/1.0'
|
||||
```
|
||||
|
||||
### Python (http.client)
|
||||
|
||||
```python
|
||||
import http.client
|
||||
import json
|
||||
|
||||
conn = http.client.HTTPSConnection('rest.gohighlevel.com')
|
||||
|
||||
headers = {
|
||||
'Authorization': 'Bearer YOUR_API_TOKEN',
|
||||
'Version': '2021-07-28',
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json',
|
||||
'User-Agent': 'YourApp/1.0',
|
||||
}
|
||||
|
||||
conn.request('GET', '/v1/campaigns/?locationId=VALUE&status=VALUE', headers=headers)
|
||||
|
||||
response = conn.getresponse()
|
||||
data = json.loads(response.read().decode())
|
||||
print(json.dumps(data, indent=2))
|
||||
conn.close()
|
||||
```
|
||||
|
||||
### n8n HTTP Node
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "GHL - GET campaigns",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"parameters": {
|
||||
"method": "GET",
|
||||
"url": "https://rest.gohighlevel.com/v1/campaigns/",
|
||||
"authentication": "genericCredentialType",
|
||||
"genericAuthType": "httpHeaderAuth",
|
||||
"sendHeaders": true,
|
||||
"headerParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "Authorization",
|
||||
"value": "Bearer YOUR_API_TOKEN"
|
||||
},
|
||||
{
|
||||
"name": "Version",
|
||||
"value": "2021-07-28"
|
||||
},
|
||||
{
|
||||
"name": "Content-Type",
|
||||
"value": "application/json"
|
||||
},
|
||||
{
|
||||
"name": "Accept",
|
||||
"value": "application/json"
|
||||
},
|
||||
{
|
||||
"name": "User-Agent",
|
||||
"value": "YourApp/1.0"
|
||||
}
|
||||
]
|
||||
},
|
||||
"sendBody": false,
|
||||
"bodyParameters": {
|
||||
"parameters": []
|
||||
},
|
||||
"options": {},
|
||||
"sendQuery": true,
|
||||
"queryParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "locationId",
|
||||
"value": "VALUE"
|
||||
},
|
||||
{
|
||||
"name": "status",
|
||||
"value": "VALUE"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**To use in n8n:**
|
||||
1. Add an HTTP Request node
|
||||
2. Switch to JSON mode (Parameters → use RAW JSON)
|
||||
3. Paste the JSON above
|
||||
4. Update `YOUR_API_TOKEN` with your actual GHL API key
|
||||
|
||||
### Request Parameters
|
||||
|
||||
| Name | Location | Type | Required | Description |
|
||||
|------|----------|------|----------|-------------|
|
||||
| `locationId` | Query | `string` | ✅ | |
|
||||
| `status` | Query | `string` | — | |
|
||||
|
||||
### Response Codes
|
||||
|
||||
| Code | Description | Schema |
|
||||
|------|-------------|--------|
|
||||
| `200` | Successful response | - **campaigns** (array of campaignsSchema) |
|
||||
| `400` | Bad Request | Standard error response for bad requests (400). Contains message, statusCode fields. |
|
||||
| `401` | Unauthorized | Standard error response for unauthorized requests (401). Contains message, statusCode fields. |
|
||||
| `422` | Unprocessable Entity | Standard error response for unprocessable entity (422). Contains message, statusCode, errors array fields. |
|
||||
|
||||
### ⚠️ Special Notes & Quirks
|
||||
|
||||
> ⚠️ **User-Agent Required:** All GHL API requests require a `User-Agent` HTTP header. Requests without it may be rejected with a 403 error.
|
||||
|
||||
---
|
||||
|
||||
|
||||
*Documentation generated from GHL OpenAPI specifications. Verify against official docs for latest changes.*
|
||||
@@ -0,0 +1,136 @@
|
||||
# GHL Companies API
|
||||
|
||||
**Endpoints:** 1
|
||||
**Base URL:** `https://rest.gohighlevel.com/v1`
|
||||
|
||||
**Version Header(s):** `2021-07-28`
|
||||
|
||||
---
|
||||
|
||||
## Endpoints
|
||||
|
||||
- [GET /companies/{companyId}](#get--companies-companyId) — Get Company
|
||||
|
||||
---
|
||||
|
||||
## GET /companies/{companyId}
|
||||
|
||||
**Summary:** Get Company
|
||||
|
||||
Get Comapny
|
||||
|
||||
**Version Header:** `2021-07-28`
|
||||
|
||||
**Operation ID:** `get-company`
|
||||
|
||||
**Tags:** Companies
|
||||
|
||||
### cURL
|
||||
|
||||
```bash
|
||||
curl -X GET 'https://rest.gohighlevel.com/v1/companies/ve9EPM428h8vShlRW1KT' \
|
||||
-H 'Authorization: Bearer YOUR_API_TOKEN' \
|
||||
-H 'Version: 2021-07-28' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-H 'Accept: application/json' \
|
||||
-H 'User-Agent: YourApp/1.0'
|
||||
```
|
||||
|
||||
### Python (http.client)
|
||||
|
||||
```python
|
||||
import http.client
|
||||
import json
|
||||
|
||||
conn = http.client.HTTPSConnection('rest.gohighlevel.com')
|
||||
|
||||
headers = {
|
||||
'Authorization': 'Bearer YOUR_API_TOKEN',
|
||||
'Version': '2021-07-28',
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json',
|
||||
'User-Agent': 'YourApp/1.0',
|
||||
}
|
||||
|
||||
conn.request('GET', '/v1/companies/ve9EPM428h8vShlRW1KT', headers=headers)
|
||||
|
||||
response = conn.getresponse()
|
||||
data = json.loads(response.read().decode())
|
||||
print(json.dumps(data, indent=2))
|
||||
conn.close()
|
||||
```
|
||||
|
||||
### n8n HTTP Node
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "GHL - GET ve9EPM428h8vShlRW1KT",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"parameters": {
|
||||
"method": "GET",
|
||||
"url": "https://rest.gohighlevel.com/v1/companies/ve9EPM428h8vShlRW1KT",
|
||||
"authentication": "genericCredentialType",
|
||||
"genericAuthType": "httpHeaderAuth",
|
||||
"sendHeaders": true,
|
||||
"headerParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "Authorization",
|
||||
"value": "Bearer YOUR_API_TOKEN"
|
||||
},
|
||||
{
|
||||
"name": "Version",
|
||||
"value": "2021-07-28"
|
||||
},
|
||||
{
|
||||
"name": "Content-Type",
|
||||
"value": "application/json"
|
||||
},
|
||||
{
|
||||
"name": "Accept",
|
||||
"value": "application/json"
|
||||
},
|
||||
{
|
||||
"name": "User-Agent",
|
||||
"value": "YourApp/1.0"
|
||||
}
|
||||
]
|
||||
},
|
||||
"sendBody": false,
|
||||
"bodyParameters": {
|
||||
"parameters": []
|
||||
},
|
||||
"options": {}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**To use in n8n:**
|
||||
1. Add an HTTP Request node
|
||||
2. Switch to JSON mode (Parameters → use RAW JSON)
|
||||
3. Paste the JSON above
|
||||
4. Update `YOUR_API_TOKEN` with your actual GHL API key
|
||||
|
||||
### Request Parameters
|
||||
|
||||
| Name | Location | Type | Required | Description |
|
||||
|------|----------|------|----------|-------------|
|
||||
| `companyId` | Path | `string` | ✅ | [example: `ve9EPM428h8vShlRW1KT`] |
|
||||
|
||||
### Response Codes
|
||||
|
||||
| Code | Description | Schema |
|
||||
|------|-------------|--------|
|
||||
| `200` | Successful response | - **company** (GetCompanyByIdSchema) |
|
||||
| `400` | Bad Request | Standard error response for bad requests (400). Contains message, statusCode fields. |
|
||||
| `401` | Unauthorized | Standard error response for unauthorized requests (401). Contains message, statusCode fields. |
|
||||
| `422` | Unprocessable Entity | Standard error response for unprocessable entity (422). Contains message, statusCode, errors array fields. |
|
||||
|
||||
### ⚠️ Special Notes & Quirks
|
||||
|
||||
> ⚠️ **User-Agent Required:** All GHL API requests require a `User-Agent` HTTP header. Requests without it may be rejected with a 403 error.
|
||||
|
||||
---
|
||||
|
||||
|
||||
*Documentation generated from GHL OpenAPI specifications. Verify against official docs for latest changes.*
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,133 @@
|
||||
# GHL Courses API
|
||||
|
||||
**Endpoints:** 1
|
||||
**Base URL:** `https://rest.gohighlevel.com/v1`
|
||||
|
||||
**Version Header(s):** `2021-07-28`
|
||||
|
||||
---
|
||||
|
||||
## Endpoints
|
||||
|
||||
- [POST /courses/courses-exporter/public/import](#post--courses-courses-exporter-public-import) — Import Courses
|
||||
|
||||
---
|
||||
|
||||
## POST /courses/courses-exporter/public/import
|
||||
|
||||
**Summary:** Import Courses
|
||||
|
||||
Import Courses through public channels
|
||||
|
||||
**Version Header:** `2021-07-28`
|
||||
|
||||
**Operation ID:** `import-courses`
|
||||
|
||||
### cURL
|
||||
|
||||
```bash
|
||||
curl -X POST 'https://rest.gohighlevel.com/v1/courses/courses-exporter/public/import' \
|
||||
-H 'Authorization: Bearer YOUR_API_TOKEN' \
|
||||
-H 'Version: 2021-07-28' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-H 'Accept: application/json' \
|
||||
-H 'User-Agent: YourApp/1.0' \
|
||||
-d '{}' # See request body schema below
|
||||
```
|
||||
|
||||
### Python (http.client)
|
||||
|
||||
```python
|
||||
import http.client
|
||||
import json
|
||||
|
||||
conn = http.client.HTTPSConnection('rest.gohighlevel.com')
|
||||
|
||||
headers = {
|
||||
'Authorization': 'Bearer YOUR_API_TOKEN',
|
||||
'Version': '2021-07-28',
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json',
|
||||
'User-Agent': 'YourApp/1.0',
|
||||
}
|
||||
|
||||
payload = json.dumps({})
|
||||
conn.request('POST', '/v1/courses/courses-exporter/public/import', body=payload, headers=headers)
|
||||
|
||||
response = conn.getresponse()
|
||||
data = json.loads(response.read().decode())
|
||||
print(json.dumps(data, indent=2))
|
||||
conn.close()
|
||||
```
|
||||
|
||||
### n8n HTTP Node
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "GHL - POST import",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"parameters": {
|
||||
"method": "POST",
|
||||
"url": "https://rest.gohighlevel.com/v1/courses/courses-exporter/public/import",
|
||||
"authentication": "genericCredentialType",
|
||||
"genericAuthType": "httpHeaderAuth",
|
||||
"sendHeaders": true,
|
||||
"headerParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "Authorization",
|
||||
"value": "Bearer YOUR_API_TOKEN"
|
||||
},
|
||||
{
|
||||
"name": "Version",
|
||||
"value": "2021-07-28"
|
||||
},
|
||||
{
|
||||
"name": "Content-Type",
|
||||
"value": "application/json"
|
||||
},
|
||||
{
|
||||
"name": "Accept",
|
||||
"value": "application/json"
|
||||
},
|
||||
{
|
||||
"name": "User-Agent",
|
||||
"value": "YourApp/1.0"
|
||||
}
|
||||
]
|
||||
},
|
||||
"sendBody": true,
|
||||
"bodyParameters": {
|
||||
"parameters": []
|
||||
},
|
||||
"options": {}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**To use in n8n:**
|
||||
1. Add an HTTP Request node
|
||||
2. Switch to JSON mode (Parameters → use RAW JSON)
|
||||
3. Paste the JSON above
|
||||
4. Update `YOUR_API_TOKEN` with your actual GHL API key
|
||||
|
||||
### Request Body Schema
|
||||
|
||||
- **locationId** (string) (required)
|
||||
- **userId** (string)
|
||||
- **products** (array of ProductInterface) (required)
|
||||
|
||||
### Response Codes
|
||||
|
||||
| Code | Description | Schema |
|
||||
|------|-------------|--------|
|
||||
| `201` | | — |
|
||||
|
||||
### ⚠️ Special Notes & Quirks
|
||||
|
||||
> ⚠️ **User-Agent Required:** All GHL API requests require a `User-Agent` HTTP header. Requests without it may be rejected with a 403 error.
|
||||
|
||||
---
|
||||
|
||||
|
||||
*Documentation generated from GHL OpenAPI specifications. Verify against official docs for latest changes.*
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,759 @@
|
||||
# GHL Emails API
|
||||
|
||||
**Endpoints:** 5
|
||||
**Base URL:** `https://rest.gohighlevel.com/v1`
|
||||
|
||||
**Version Header(s):** `2021-07-28`
|
||||
|
||||
---
|
||||
|
||||
## Endpoints
|
||||
|
||||
- [GET /emails/builder](#get--emails-builder) — Fetch email templates
|
||||
- [POST /emails/builder](#post--emails-builder) — Create a new template
|
||||
- [POST /emails/builder/data](#post--emails-builder-data) — Update a template
|
||||
- [DELETE /emails/builder/{locationId}/{templateId}](#delete--emails-builder-locationId-templateId) — Delete a template
|
||||
- [GET /emails/schedule](#get--emails-schedule) — Get Campaigns
|
||||
|
||||
---
|
||||
|
||||
## GET /emails/builder
|
||||
|
||||
**Summary:** Fetch email templates
|
||||
|
||||
Fetch email templates by location id
|
||||
|
||||
**Version Header:** `2021-07-28`
|
||||
|
||||
**Operation ID:** `fetch-template`
|
||||
|
||||
**Tags:** Templates
|
||||
|
||||
### cURL
|
||||
|
||||
```bash
|
||||
curl -X GET 'https://rest.gohighlevel.com/v1/emails/builder?locationId=VALUE&limit=VALUE&offset=VALUE&search=VALUE&sortByDate=VALUE&archived=VALUE&builderVersion=1&name=VALUE&parentId=VALUE&originId=VALUE&templatesOnly=VALUE' \
|
||||
-H 'Authorization: Bearer YOUR_API_TOKEN' \
|
||||
-H 'Version: 2021-07-28' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-H 'Accept: application/json' \
|
||||
-H 'User-Agent: YourApp/1.0'
|
||||
```
|
||||
|
||||
### Python (http.client)
|
||||
|
||||
```python
|
||||
import http.client
|
||||
import json
|
||||
|
||||
conn = http.client.HTTPSConnection('rest.gohighlevel.com')
|
||||
|
||||
headers = {
|
||||
'Authorization': 'Bearer YOUR_API_TOKEN',
|
||||
'Version': '2021-07-28',
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json',
|
||||
'User-Agent': 'YourApp/1.0',
|
||||
}
|
||||
|
||||
conn.request('GET', '/v1/emails/builder?locationId=VALUE&limit=VALUE&offset=VALUE&search=VALUE&sortByDate=VALUE&archived=VALUE&builderVersion=VALUE&name=VALUE&parentId=VALUE&originId=VALUE&templatesOnly=VALUE', headers=headers)
|
||||
|
||||
response = conn.getresponse()
|
||||
data = json.loads(response.read().decode())
|
||||
print(json.dumps(data, indent=2))
|
||||
conn.close()
|
||||
```
|
||||
|
||||
### n8n HTTP Node
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "GHL - GET builder",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"parameters": {
|
||||
"method": "GET",
|
||||
"url": "https://rest.gohighlevel.com/v1/emails/builder",
|
||||
"authentication": "genericCredentialType",
|
||||
"genericAuthType": "httpHeaderAuth",
|
||||
"sendHeaders": true,
|
||||
"headerParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "Authorization",
|
||||
"value": "Bearer YOUR_API_TOKEN"
|
||||
},
|
||||
{
|
||||
"name": "Version",
|
||||
"value": "2021-07-28"
|
||||
},
|
||||
{
|
||||
"name": "Content-Type",
|
||||
"value": "application/json"
|
||||
},
|
||||
{
|
||||
"name": "Accept",
|
||||
"value": "application/json"
|
||||
},
|
||||
{
|
||||
"name": "User-Agent",
|
||||
"value": "YourApp/1.0"
|
||||
}
|
||||
]
|
||||
},
|
||||
"sendBody": false,
|
||||
"bodyParameters": {
|
||||
"parameters": []
|
||||
},
|
||||
"options": {},
|
||||
"sendQuery": true,
|
||||
"queryParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "locationId",
|
||||
"value": "VALUE"
|
||||
},
|
||||
{
|
||||
"name": "limit",
|
||||
"value": "VALUE"
|
||||
},
|
||||
{
|
||||
"name": "offset",
|
||||
"value": "VALUE"
|
||||
},
|
||||
{
|
||||
"name": "search",
|
||||
"value": "VALUE"
|
||||
},
|
||||
{
|
||||
"name": "sortByDate",
|
||||
"value": "VALUE"
|
||||
},
|
||||
{
|
||||
"name": "archived",
|
||||
"value": "VALUE"
|
||||
},
|
||||
{
|
||||
"name": "builderVersion",
|
||||
"value": "VALUE"
|
||||
},
|
||||
{
|
||||
"name": "name",
|
||||
"value": "VALUE"
|
||||
},
|
||||
{
|
||||
"name": "parentId",
|
||||
"value": "VALUE"
|
||||
},
|
||||
{
|
||||
"name": "originId",
|
||||
"value": "VALUE"
|
||||
},
|
||||
{
|
||||
"name": "templatesOnly",
|
||||
"value": "VALUE"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**To use in n8n:**
|
||||
1. Add an HTTP Request node
|
||||
2. Switch to JSON mode (Parameters → use RAW JSON)
|
||||
3. Paste the JSON above
|
||||
4. Update `YOUR_API_TOKEN` with your actual GHL API key
|
||||
|
||||
### Request Parameters
|
||||
|
||||
| Name | Location | Type | Required | Description |
|
||||
|------|----------|------|----------|-------------|
|
||||
| `locationId` | Query | `string` | ✅ | |
|
||||
| `limit` | Query | `string` | — | |
|
||||
| `offset` | Query | `string` | — | |
|
||||
| `search` | Query | `string` | — | |
|
||||
| `sortByDate` | Query | `string` | — | |
|
||||
| `archived` | Query | `string` | — | |
|
||||
| `builderVersion` | Query | `string` | — | (values: 1, 2) |
|
||||
| `name` | Query | `string` | — | |
|
||||
| `parentId` | Query | `string` | — | |
|
||||
| `originId` | Query | `string` | — | |
|
||||
| `templatesOnly` | Query | `string` | — | |
|
||||
|
||||
### Response Codes
|
||||
|
||||
| Code | Description | Schema |
|
||||
|------|-------------|--------|
|
||||
| `200` | Success | - **name** (string) (e.g. `New Template`): template name<br> - **updatedBy** (string) (e.g. `John Doe`): updated by<br> - **isPlainText** (boolean) (... |
|
||||
| `400` | Bad Request | Standard error response for bad requests (400). Contains message, statusCode fields. |
|
||||
| `401` | Unauthorized | Standard error response for unauthorized requests (401). Contains message, statusCode fields. |
|
||||
| `404` | Not Found | — |
|
||||
| `422` | Unprocessable Entity | Standard error response for unprocessable entity (422). Contains message, statusCode, errors array fields. |
|
||||
|
||||
### ⚠️ Special Notes & Quirks
|
||||
|
||||
> ⚠️ **User-Agent Required:** All GHL API requests require a `User-Agent` HTTP header. Requests without it may be rejected with a 403 error.
|
||||
|
||||
---
|
||||
|
||||
## POST /emails/builder
|
||||
|
||||
**Summary:** Create a new template
|
||||
|
||||
Create a new template
|
||||
|
||||
**Version Header:** `2021-07-28`
|
||||
|
||||
**Operation ID:** `create-template`
|
||||
|
||||
**Tags:** Templates
|
||||
|
||||
### cURL
|
||||
|
||||
```bash
|
||||
curl -X POST 'https://rest.gohighlevel.com/v1/emails/builder' \
|
||||
-H 'Authorization: Bearer YOUR_API_TOKEN' \
|
||||
-H 'Version: 2021-07-28' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-H 'Accept: application/json' \
|
||||
-H 'User-Agent: YourApp/1.0' \
|
||||
-d '{}' # See request body schema below
|
||||
```
|
||||
|
||||
### Python (http.client)
|
||||
|
||||
```python
|
||||
import http.client
|
||||
import json
|
||||
|
||||
conn = http.client.HTTPSConnection('rest.gohighlevel.com')
|
||||
|
||||
headers = {
|
||||
'Authorization': 'Bearer YOUR_API_TOKEN',
|
||||
'Version': '2021-07-28',
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json',
|
||||
'User-Agent': 'YourApp/1.0',
|
||||
}
|
||||
|
||||
payload = json.dumps({})
|
||||
conn.request('POST', '/v1/emails/builder', body=payload, headers=headers)
|
||||
|
||||
response = conn.getresponse()
|
||||
data = json.loads(response.read().decode())
|
||||
print(json.dumps(data, indent=2))
|
||||
conn.close()
|
||||
```
|
||||
|
||||
### n8n HTTP Node
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "GHL - POST builder",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"parameters": {
|
||||
"method": "POST",
|
||||
"url": "https://rest.gohighlevel.com/v1/emails/builder",
|
||||
"authentication": "genericCredentialType",
|
||||
"genericAuthType": "httpHeaderAuth",
|
||||
"sendHeaders": true,
|
||||
"headerParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "Authorization",
|
||||
"value": "Bearer YOUR_API_TOKEN"
|
||||
},
|
||||
{
|
||||
"name": "Version",
|
||||
"value": "2021-07-28"
|
||||
},
|
||||
{
|
||||
"name": "Content-Type",
|
||||
"value": "application/json"
|
||||
},
|
||||
{
|
||||
"name": "Accept",
|
||||
"value": "application/json"
|
||||
},
|
||||
{
|
||||
"name": "User-Agent",
|
||||
"value": "YourApp/1.0"
|
||||
}
|
||||
]
|
||||
},
|
||||
"sendBody": true,
|
||||
"bodyParameters": {
|
||||
"parameters": []
|
||||
},
|
||||
"options": {}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**To use in n8n:**
|
||||
1. Add an HTTP Request node
|
||||
2. Switch to JSON mode (Parameters → use RAW JSON)
|
||||
3. Paste the JSON above
|
||||
4. Update `YOUR_API_TOKEN` with your actual GHL API key
|
||||
|
||||
### Request Body Schema
|
||||
|
||||
- **locationId** (string) (required) (e.g. `ve9EPM428h8vShlRW1KT`)
|
||||
- **title** (string) (e.g. `template title`)
|
||||
- **type** (string) (required)
|
||||
- **updatedBy** (string) (e.g. `zYy3YOUuHxgomU1uYJty`)
|
||||
- **builderVersion** (string)
|
||||
- **name** (string) (e.g. `Template1`)
|
||||
- **parentId** (string) (e.g. `zYy3YOUuHxgomU1uYJty`)
|
||||
- **templateDataUrl** (string) (e.g. ``)
|
||||
- **importProvider** (string) (required)
|
||||
- **importURL** (string) (e.g. `https://tplshare.com/fhYJ3Mi`)
|
||||
- **templateSource** (string) (e.g. `template_library`)
|
||||
- **isPlainText** (boolean) (e.g. `False`)
|
||||
|
||||
### Response Codes
|
||||
|
||||
| Code | Description | Schema |
|
||||
|------|-------------|--------|
|
||||
| `201` | Success | - **redirect** (string) (required) (e.g. `66e811229245fc098765590`): template id<br> - **traceId** (string) (required) (e.g. `0c52e980-41f6-4be7-8c... |
|
||||
| `400` | Bad Request | Standard error response for bad requests (400). Contains message, statusCode fields. |
|
||||
| `401` | Unauthorized | Standard error response for unauthorized requests (401). Contains message, statusCode fields. |
|
||||
| `404` | Not Found | — |
|
||||
| `422` | Unprocessable Entity | Standard error response for unprocessable entity (422). Contains message, statusCode, errors array fields. |
|
||||
|
||||
### ⚠️ Special Notes & Quirks
|
||||
|
||||
> ⚠️ **User-Agent Required:** All GHL API requests require a `User-Agent` HTTP header. Requests without it may be rejected with a 403 error.
|
||||
|
||||
---
|
||||
|
||||
## POST /emails/builder/data
|
||||
|
||||
**Summary:** Update a template
|
||||
|
||||
Update a template
|
||||
|
||||
**Version Header:** `2021-07-28`
|
||||
|
||||
**Operation ID:** `update-template`
|
||||
|
||||
**Tags:** Templates
|
||||
|
||||
### cURL
|
||||
|
||||
```bash
|
||||
curl -X POST 'https://rest.gohighlevel.com/v1/emails/builder/data' \
|
||||
-H 'Authorization: Bearer YOUR_API_TOKEN' \
|
||||
-H 'Version: 2021-07-28' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-H 'Accept: application/json' \
|
||||
-H 'User-Agent: YourApp/1.0' \
|
||||
-d '{}' # See request body schema below
|
||||
```
|
||||
|
||||
### Python (http.client)
|
||||
|
||||
```python
|
||||
import http.client
|
||||
import json
|
||||
|
||||
conn = http.client.HTTPSConnection('rest.gohighlevel.com')
|
||||
|
||||
headers = {
|
||||
'Authorization': 'Bearer YOUR_API_TOKEN',
|
||||
'Version': '2021-07-28',
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json',
|
||||
'User-Agent': 'YourApp/1.0',
|
||||
}
|
||||
|
||||
payload = json.dumps({})
|
||||
conn.request('POST', '/v1/emails/builder/data', body=payload, headers=headers)
|
||||
|
||||
response = conn.getresponse()
|
||||
data = json.loads(response.read().decode())
|
||||
print(json.dumps(data, indent=2))
|
||||
conn.close()
|
||||
```
|
||||
|
||||
### n8n HTTP Node
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "GHL - POST data",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"parameters": {
|
||||
"method": "POST",
|
||||
"url": "https://rest.gohighlevel.com/v1/emails/builder/data",
|
||||
"authentication": "genericCredentialType",
|
||||
"genericAuthType": "httpHeaderAuth",
|
||||
"sendHeaders": true,
|
||||
"headerParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "Authorization",
|
||||
"value": "Bearer YOUR_API_TOKEN"
|
||||
},
|
||||
{
|
||||
"name": "Version",
|
||||
"value": "2021-07-28"
|
||||
},
|
||||
{
|
||||
"name": "Content-Type",
|
||||
"value": "application/json"
|
||||
},
|
||||
{
|
||||
"name": "Accept",
|
||||
"value": "application/json"
|
||||
},
|
||||
{
|
||||
"name": "User-Agent",
|
||||
"value": "YourApp/1.0"
|
||||
}
|
||||
]
|
||||
},
|
||||
"sendBody": true,
|
||||
"bodyParameters": {
|
||||
"parameters": []
|
||||
},
|
||||
"options": {}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**To use in n8n:**
|
||||
1. Add an HTTP Request node
|
||||
2. Switch to JSON mode (Parameters → use RAW JSON)
|
||||
3. Paste the JSON above
|
||||
4. Update `YOUR_API_TOKEN` with your actual GHL API key
|
||||
|
||||
### Request Body Schema
|
||||
|
||||
- **locationId** (string) (required) (e.g. `ve9EPM428h8vShlRW1KT`)
|
||||
- **templateId** (string) (required) (e.g. `zYy3YOUuHxgomU1uYJty`)
|
||||
- **updatedBy** (string) (required) (e.g. `zYy3YOUuHxgomU1uYJty`)
|
||||
- **dnd** (string) (required) (e.g. `{elements:[], attrs:{}, templateSettings:{}}`)
|
||||
- **html** (string) (required) (e.g. ``)
|
||||
- **editorType** (string) (required)
|
||||
- **previewText** (string) (e.g. `zYy3YOUuHxgomU1uYJty`)
|
||||
- **isPlainText** (boolean) (e.g. `false`)
|
||||
|
||||
### Response Codes
|
||||
|
||||
| Code | Description | Schema |
|
||||
|------|-------------|--------|
|
||||
| `201` | Success | - **ok** (string) (e.g. `true`): ok<br> - **traceId** (string) (e.g. `0c52e980-41f6-4be7-8c4b-32332ss`): trace id<br> - **previewUrl** (string) (e.g.... |
|
||||
| `400` | Bad Request | Standard error response for bad requests (400). Contains message, statusCode fields. |
|
||||
| `401` | Unauthorized | Standard error response for unauthorized requests (401). Contains message, statusCode fields. |
|
||||
| `404` | Not Found | — |
|
||||
| `422` | Unprocessable Entity | Standard error response for unprocessable entity (422). Contains message, statusCode, errors array fields. |
|
||||
|
||||
### ⚠️ Special Notes & Quirks
|
||||
|
||||
> ⚠️ **User-Agent Required:** All GHL API requests require a `User-Agent` HTTP header. Requests without it may be rejected with a 403 error.
|
||||
|
||||
---
|
||||
|
||||
## DELETE /emails/builder/{locationId}/{templateId}
|
||||
|
||||
**Summary:** Delete a template
|
||||
|
||||
Delete a template
|
||||
|
||||
**Version Header:** `2021-07-28`
|
||||
|
||||
**Operation ID:** `delete-template`
|
||||
|
||||
**Tags:** Templates
|
||||
|
||||
### cURL
|
||||
|
||||
```bash
|
||||
curl -X DELETE 'https://rest.gohighlevel.com/v1/emails/builder/VALUE/VALUE' \
|
||||
-H 'Authorization: Bearer YOUR_API_TOKEN' \
|
||||
-H 'Version: 2021-07-28' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-H 'Accept: application/json' \
|
||||
-H 'User-Agent: YourApp/1.0'
|
||||
```
|
||||
|
||||
### Python (http.client)
|
||||
|
||||
```python
|
||||
import http.client
|
||||
import json
|
||||
|
||||
conn = http.client.HTTPSConnection('rest.gohighlevel.com')
|
||||
|
||||
headers = {
|
||||
'Authorization': 'Bearer YOUR_API_TOKEN',
|
||||
'Version': '2021-07-28',
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json',
|
||||
'User-Agent': 'YourApp/1.0',
|
||||
}
|
||||
|
||||
conn.request('DELETE', '/v1/emails/builder/VALUE/VALUE', headers=headers)
|
||||
|
||||
response = conn.getresponse()
|
||||
data = json.loads(response.read().decode())
|
||||
print(json.dumps(data, indent=2))
|
||||
conn.close()
|
||||
```
|
||||
|
||||
### n8n HTTP Node
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "GHL - DELETE VALUE",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"parameters": {
|
||||
"method": "DELETE",
|
||||
"url": "https://rest.gohighlevel.com/v1/emails/builder/VALUE/VALUE",
|
||||
"authentication": "genericCredentialType",
|
||||
"genericAuthType": "httpHeaderAuth",
|
||||
"sendHeaders": true,
|
||||
"headerParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "Authorization",
|
||||
"value": "Bearer YOUR_API_TOKEN"
|
||||
},
|
||||
{
|
||||
"name": "Version",
|
||||
"value": "2021-07-28"
|
||||
},
|
||||
{
|
||||
"name": "Content-Type",
|
||||
"value": "application/json"
|
||||
},
|
||||
{
|
||||
"name": "Accept",
|
||||
"value": "application/json"
|
||||
},
|
||||
{
|
||||
"name": "User-Agent",
|
||||
"value": "YourApp/1.0"
|
||||
}
|
||||
]
|
||||
},
|
||||
"sendBody": false,
|
||||
"bodyParameters": {
|
||||
"parameters": []
|
||||
},
|
||||
"options": {}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**To use in n8n:**
|
||||
1. Add an HTTP Request node
|
||||
2. Switch to JSON mode (Parameters → use RAW JSON)
|
||||
3. Paste the JSON above
|
||||
4. Update `YOUR_API_TOKEN` with your actual GHL API key
|
||||
|
||||
### Request Parameters
|
||||
|
||||
| Name | Location | Type | Required | Description |
|
||||
|------|----------|------|----------|-------------|
|
||||
| `locationId` | Path | `string` | ✅ | |
|
||||
| `templateId` | Path | `string` | ✅ | |
|
||||
|
||||
### Response Codes
|
||||
|
||||
| Code | Description | Schema |
|
||||
|------|-------------|--------|
|
||||
| `200` | Success | - **ok** (string) (e.g. `true`): ok<br> - **traceId** (string) (e.g. `0c52e980-41f6-4be7-8c4b-32332ss`): trace id |
|
||||
| `400` | Bad Request | Standard error response for bad requests (400). Contains message, statusCode fields. |
|
||||
| `401` | Unauthorized | Standard error response for unauthorized requests (401). Contains message, statusCode fields. |
|
||||
| `404` | Not Found | — |
|
||||
| `422` | Unprocessable Entity | Standard error response for unprocessable entity (422). Contains message, statusCode, errors array fields. |
|
||||
|
||||
### ⚠️ Special Notes & Quirks
|
||||
|
||||
> ⚠️ **User-Agent Required:** All GHL API requests require a `User-Agent` HTTP header. Requests without it may be rejected with a 403 error.
|
||||
|
||||
---
|
||||
|
||||
## GET /emails/schedule
|
||||
|
||||
**Summary:** Get Campaigns
|
||||
|
||||
Get Campaigns
|
||||
|
||||
**Version Header:** `2021-07-28`
|
||||
|
||||
**Operation ID:** `fetch-campaigns`
|
||||
|
||||
**Tags:** Campaigns
|
||||
|
||||
### cURL
|
||||
|
||||
```bash
|
||||
curl -X GET 'https://rest.gohighlevel.com/v1/emails/schedule?locationId=VALUE&limit=VALUE&offset=VALUE&status=active&emailStatus=all&name=VALUE&parentId=VALUE&limitedFields=VALUE&archived=VALUE&campaignsOnly=VALUE&showStats=VALUE' \
|
||||
-H 'Authorization: Bearer YOUR_API_TOKEN' \
|
||||
-H 'Version: 2021-07-28' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-H 'Accept: application/json' \
|
||||
-H 'User-Agent: YourApp/1.0'
|
||||
```
|
||||
|
||||
### Python (http.client)
|
||||
|
||||
```python
|
||||
import http.client
|
||||
import json
|
||||
|
||||
conn = http.client.HTTPSConnection('rest.gohighlevel.com')
|
||||
|
||||
headers = {
|
||||
'Authorization': 'Bearer YOUR_API_TOKEN',
|
||||
'Version': '2021-07-28',
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json',
|
||||
'User-Agent': 'YourApp/1.0',
|
||||
}
|
||||
|
||||
conn.request('GET', '/v1/emails/schedule?locationId=VALUE&limit=VALUE&offset=VALUE&status=VALUE&emailStatus=VALUE&name=VALUE&parentId=VALUE&limitedFields=VALUE&archived=VALUE&campaignsOnly=VALUE&showStats=VALUE', headers=headers)
|
||||
|
||||
response = conn.getresponse()
|
||||
data = json.loads(response.read().decode())
|
||||
print(json.dumps(data, indent=2))
|
||||
conn.close()
|
||||
```
|
||||
|
||||
### n8n HTTP Node
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "GHL - GET schedule",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"parameters": {
|
||||
"method": "GET",
|
||||
"url": "https://rest.gohighlevel.com/v1/emails/schedule",
|
||||
"authentication": "genericCredentialType",
|
||||
"genericAuthType": "httpHeaderAuth",
|
||||
"sendHeaders": true,
|
||||
"headerParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "Authorization",
|
||||
"value": "Bearer YOUR_API_TOKEN"
|
||||
},
|
||||
{
|
||||
"name": "Version",
|
||||
"value": "2021-07-28"
|
||||
},
|
||||
{
|
||||
"name": "Content-Type",
|
||||
"value": "application/json"
|
||||
},
|
||||
{
|
||||
"name": "Accept",
|
||||
"value": "application/json"
|
||||
},
|
||||
{
|
||||
"name": "User-Agent",
|
||||
"value": "YourApp/1.0"
|
||||
}
|
||||
]
|
||||
},
|
||||
"sendBody": false,
|
||||
"bodyParameters": {
|
||||
"parameters": []
|
||||
},
|
||||
"options": {},
|
||||
"sendQuery": true,
|
||||
"queryParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "locationId",
|
||||
"value": "VALUE"
|
||||
},
|
||||
{
|
||||
"name": "limit",
|
||||
"value": "VALUE"
|
||||
},
|
||||
{
|
||||
"name": "offset",
|
||||
"value": "VALUE"
|
||||
},
|
||||
{
|
||||
"name": "status",
|
||||
"value": "VALUE"
|
||||
},
|
||||
{
|
||||
"name": "emailStatus",
|
||||
"value": "VALUE"
|
||||
},
|
||||
{
|
||||
"name": "name",
|
||||
"value": "VALUE"
|
||||
},
|
||||
{
|
||||
"name": "parentId",
|
||||
"value": "VALUE"
|
||||
},
|
||||
{
|
||||
"name": "limitedFields",
|
||||
"value": "VALUE"
|
||||
},
|
||||
{
|
||||
"name": "archived",
|
||||
"value": "VALUE"
|
||||
},
|
||||
{
|
||||
"name": "campaignsOnly",
|
||||
"value": "VALUE"
|
||||
},
|
||||
{
|
||||
"name": "showStats",
|
||||
"value": "VALUE"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**To use in n8n:**
|
||||
1. Add an HTTP Request node
|
||||
2. Switch to JSON mode (Parameters → use RAW JSON)
|
||||
3. Paste the JSON above
|
||||
4. Update `YOUR_API_TOKEN` with your actual GHL API key
|
||||
|
||||
### Request Parameters
|
||||
|
||||
| Name | Location | Type | Required | Description |
|
||||
|------|----------|------|----------|-------------|
|
||||
| `locationId` | Query | `string` | ✅ | Location ID to fetch campaigns from |
|
||||
| `limit` | Query | `number` | — | Maximum number of campaigns to return. Defaults to 10, maximum is 100 |
|
||||
| `offset` | Query | `number` | — | Number of campaigns to skip for pagination |
|
||||
| `status` | Query | `string` | — | Filter by schedule status (values: active, pause, complete, cancelled, retry, draft, resend-sched... |
|
||||
| `emailStatus` | Query | `string` | — | Filter by email delivery status (values: all, not-started, paused, cancelled, processing, resumed... |
|
||||
| `name` | Query | `string` | — | Filter campaigns by name |
|
||||
| `parentId` | Query | `string` | — | Filter campaigns by parent folder ID |
|
||||
| `limitedFields` | Query | `boolean` | — | When true, returns only essential campaign fields like id, templateDataDownloadUrl, updatedAt, ty... |
|
||||
| `archived` | Query | `boolean` | — | Filter archived campaigns |
|
||||
| `campaignsOnly` | Query | `boolean` | — | Return only campaigns, excluding folders |
|
||||
| `showStats` | Query | `boolean` | — | When true, returns campaign statistics including delivered count, opened count, clicked count and... |
|
||||
|
||||
### Response Codes
|
||||
|
||||
| Code | Description | Schema |
|
||||
|------|-------------|--------|
|
||||
| `200` | Success | - **schedules** (array of ScheduleDto) (required): The list of campaigns<br> - **total** (array) (required): The total number of campaigns<br> - **tr... |
|
||||
| `400` | Bad Request | Standard error response for bad requests (400). Contains message, statusCode fields. |
|
||||
| `401` | Unauthorized | Standard error response for unauthorized requests (401). Contains message, statusCode fields. |
|
||||
| `403` | The token does not have access to this location | - **statusCode** (number) (e.g. `403`)<br> - **message** (string) (e.g. `The token does not have access to this location`) |
|
||||
| `404` | Not Found | - **statusCode** (number) (e.g. `404`)<br> - **message** (string) (e.g. `Not Found`)<br> - **error** (string) (e.g. `The requested resource was not f... |
|
||||
| `422` | Unprocessable Entity | Standard error response for unprocessable entity (422). Contains message, statusCode, errors array fields. |
|
||||
|
||||
### ⚠️ Special Notes & Quirks
|
||||
|
||||
> ⚠️ **User-Agent Required:** All GHL API requests require a `User-Agent` HTTP header. Requests without it may be rejected with a 403 error.
|
||||
|
||||
---
|
||||
|
||||
|
||||
*Documentation generated from GHL OpenAPI specifications. Verify against official docs for latest changes.*
|
||||
@@ -0,0 +1,451 @@
|
||||
# GHL Forms API
|
||||
|
||||
**Endpoints:** 3
|
||||
**Base URL:** `https://rest.gohighlevel.com/v1`
|
||||
|
||||
**Version Header(s):** `2021-07-28`
|
||||
|
||||
---
|
||||
|
||||
## Endpoints
|
||||
|
||||
- [GET /forms/](#get--forms-) — Get Forms
|
||||
- [GET /forms/submissions](#get--forms-submissions) — Get Forms Submissions
|
||||
- [POST /forms/upload-custom-files](#post--forms-upload-custom-files) — Upload files to custom fields
|
||||
|
||||
---
|
||||
|
||||
## GET /forms/
|
||||
|
||||
**Summary:** Get Forms
|
||||
|
||||
Get Forms
|
||||
|
||||
**Version Header:** `2021-07-28`
|
||||
|
||||
**Operation ID:** `get-forms`
|
||||
|
||||
**Tags:** Forms
|
||||
|
||||
### cURL
|
||||
|
||||
```bash
|
||||
curl -X GET 'https://rest.gohighlevel.com/v1/forms/?locationId=VALUE&skip=VALUE&limit=VALUE&type=VALUE' \
|
||||
-H 'Authorization: Bearer YOUR_API_TOKEN' \
|
||||
-H 'Version: 2021-07-28' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-H 'Accept: application/json' \
|
||||
-H 'User-Agent: YourApp/1.0'
|
||||
```
|
||||
|
||||
### Python (http.client)
|
||||
|
||||
```python
|
||||
import http.client
|
||||
import json
|
||||
|
||||
conn = http.client.HTTPSConnection('rest.gohighlevel.com')
|
||||
|
||||
headers = {
|
||||
'Authorization': 'Bearer YOUR_API_TOKEN',
|
||||
'Version': '2021-07-28',
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json',
|
||||
'User-Agent': 'YourApp/1.0',
|
||||
}
|
||||
|
||||
conn.request('GET', '/v1/forms/?locationId=VALUE&skip=VALUE&limit=VALUE&type=VALUE', headers=headers)
|
||||
|
||||
response = conn.getresponse()
|
||||
data = json.loads(response.read().decode())
|
||||
print(json.dumps(data, indent=2))
|
||||
conn.close()
|
||||
```
|
||||
|
||||
### n8n HTTP Node
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "GHL - GET forms",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"parameters": {
|
||||
"method": "GET",
|
||||
"url": "https://rest.gohighlevel.com/v1/forms/",
|
||||
"authentication": "genericCredentialType",
|
||||
"genericAuthType": "httpHeaderAuth",
|
||||
"sendHeaders": true,
|
||||
"headerParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "Authorization",
|
||||
"value": "Bearer YOUR_API_TOKEN"
|
||||
},
|
||||
{
|
||||
"name": "Version",
|
||||
"value": "2021-07-28"
|
||||
},
|
||||
{
|
||||
"name": "Content-Type",
|
||||
"value": "application/json"
|
||||
},
|
||||
{
|
||||
"name": "Accept",
|
||||
"value": "application/json"
|
||||
},
|
||||
{
|
||||
"name": "User-Agent",
|
||||
"value": "YourApp/1.0"
|
||||
}
|
||||
]
|
||||
},
|
||||
"sendBody": false,
|
||||
"bodyParameters": {
|
||||
"parameters": []
|
||||
},
|
||||
"options": {},
|
||||
"sendQuery": true,
|
||||
"queryParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "locationId",
|
||||
"value": "VALUE"
|
||||
},
|
||||
{
|
||||
"name": "skip",
|
||||
"value": "VALUE"
|
||||
},
|
||||
{
|
||||
"name": "limit",
|
||||
"value": "VALUE"
|
||||
},
|
||||
{
|
||||
"name": "type",
|
||||
"value": "VALUE"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**To use in n8n:**
|
||||
1. Add an HTTP Request node
|
||||
2. Switch to JSON mode (Parameters → use RAW JSON)
|
||||
3. Paste the JSON above
|
||||
4. Update `YOUR_API_TOKEN` with your actual GHL API key
|
||||
|
||||
### Request Parameters
|
||||
|
||||
| Name | Location | Type | Required | Description |
|
||||
|------|----------|------|----------|-------------|
|
||||
| `locationId` | Query | `string` | ✅ | |
|
||||
| `skip` | Query | `number` | — | |
|
||||
| `limit` | Query | `number` | — | Limit Per Page records count. will allow maximum up to 50 and default will be 10 |
|
||||
| `type` | Query | `string` | — | |
|
||||
|
||||
### Response Codes
|
||||
|
||||
| Code | Description | Schema |
|
||||
|------|-------------|--------|
|
||||
| `200` | Successful response | - **forms** (array of FormsParams)<br> - **total** (number) (e.g. `20`): Total number of forms |
|
||||
| `400` | Bad Request | Standard error response for bad requests (400). Contains message, statusCode fields. |
|
||||
| `401` | Unauthorized | Standard error response for unauthorized requests (401). Contains message, statusCode fields. |
|
||||
|
||||
### ⚠️ Special Notes & Quirks
|
||||
|
||||
> ⚠️ **User-Agent Required:** All GHL API requests require a `User-Agent` HTTP header. Requests without it may be rejected with a 403 error.
|
||||
|
||||
---
|
||||
|
||||
## GET /forms/submissions
|
||||
|
||||
**Summary:** Get Forms Submissions
|
||||
|
||||
Get Forms Submissions
|
||||
|
||||
**Version Header:** `2021-07-28`
|
||||
|
||||
**Operation ID:** `get-forms-submissions`
|
||||
|
||||
**Tags:** Forms
|
||||
|
||||
### cURL
|
||||
|
||||
```bash
|
||||
curl -X GET 'https://rest.gohighlevel.com/v1/forms/submissions?locationId=VALUE&page=VALUE&limit=VALUE&formId=VALUE&q=VALUE&startAt=VALUE&endAt=VALUE' \
|
||||
-H 'Authorization: Bearer YOUR_API_TOKEN' \
|
||||
-H 'Version: 2021-07-28' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-H 'Accept: application/json' \
|
||||
-H 'User-Agent: YourApp/1.0'
|
||||
```
|
||||
|
||||
### Python (http.client)
|
||||
|
||||
```python
|
||||
import http.client
|
||||
import json
|
||||
|
||||
conn = http.client.HTTPSConnection('rest.gohighlevel.com')
|
||||
|
||||
headers = {
|
||||
'Authorization': 'Bearer YOUR_API_TOKEN',
|
||||
'Version': '2021-07-28',
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json',
|
||||
'User-Agent': 'YourApp/1.0',
|
||||
}
|
||||
|
||||
conn.request('GET', '/v1/forms/submissions?locationId=VALUE&page=VALUE&limit=VALUE&formId=VALUE&q=VALUE&startAt=VALUE&endAt=VALUE', headers=headers)
|
||||
|
||||
response = conn.getresponse()
|
||||
data = json.loads(response.read().decode())
|
||||
print(json.dumps(data, indent=2))
|
||||
conn.close()
|
||||
```
|
||||
|
||||
### n8n HTTP Node
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "GHL - GET submissions",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"parameters": {
|
||||
"method": "GET",
|
||||
"url": "https://rest.gohighlevel.com/v1/forms/submissions",
|
||||
"authentication": "genericCredentialType",
|
||||
"genericAuthType": "httpHeaderAuth",
|
||||
"sendHeaders": true,
|
||||
"headerParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "Authorization",
|
||||
"value": "Bearer YOUR_API_TOKEN"
|
||||
},
|
||||
{
|
||||
"name": "Version",
|
||||
"value": "2021-07-28"
|
||||
},
|
||||
{
|
||||
"name": "Content-Type",
|
||||
"value": "application/json"
|
||||
},
|
||||
{
|
||||
"name": "Accept",
|
||||
"value": "application/json"
|
||||
},
|
||||
{
|
||||
"name": "User-Agent",
|
||||
"value": "YourApp/1.0"
|
||||
}
|
||||
]
|
||||
},
|
||||
"sendBody": false,
|
||||
"bodyParameters": {
|
||||
"parameters": []
|
||||
},
|
||||
"options": {},
|
||||
"sendQuery": true,
|
||||
"queryParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "locationId",
|
||||
"value": "VALUE"
|
||||
},
|
||||
{
|
||||
"name": "page",
|
||||
"value": "VALUE"
|
||||
},
|
||||
{
|
||||
"name": "limit",
|
||||
"value": "VALUE"
|
||||
},
|
||||
{
|
||||
"name": "formId",
|
||||
"value": "VALUE"
|
||||
},
|
||||
{
|
||||
"name": "q",
|
||||
"value": "VALUE"
|
||||
},
|
||||
{
|
||||
"name": "startAt",
|
||||
"value": "VALUE"
|
||||
},
|
||||
{
|
||||
"name": "endAt",
|
||||
"value": "VALUE"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**To use in n8n:**
|
||||
1. Add an HTTP Request node
|
||||
2. Switch to JSON mode (Parameters → use RAW JSON)
|
||||
3. Paste the JSON above
|
||||
4. Update `YOUR_API_TOKEN` with your actual GHL API key
|
||||
|
||||
### Request Parameters
|
||||
|
||||
| Name | Location | Type | Required | Description |
|
||||
|------|----------|------|----------|-------------|
|
||||
| `locationId` | Query | `string` | ✅ | |
|
||||
| `page` | Query | `number` | — | Page No. By default it will be 1 |
|
||||
| `limit` | Query | `number` | — | Limit Per Page records count. will allow maximum up to 100 and default will be 20 |
|
||||
| `formId` | Query | `string` | — | Filter submission by form id |
|
||||
| `q` | Query | `string` | — | Filter by contactId, name, email or phone no. |
|
||||
| `startAt` | Query | `string` | — | Get submission by starting of this date. By default it will be same date of last month(YYYY-MM-DD). |
|
||||
| `endAt` | Query | `string` | — | Get submission by ending of this date. By default it will be current date(YYYY-MM-DD). |
|
||||
|
||||
### Response Codes
|
||||
|
||||
| Code | Description | Schema |
|
||||
|------|-------------|--------|
|
||||
| `200` | Successful response | - **submissions** (array of FormsSubmissionsSubmissionsSchema)<br> - **meta** (metaSchema) |
|
||||
| `400` | Bad Request | Standard error response for bad requests (400). Contains message, statusCode fields. |
|
||||
| `401` | Unauthorized | Standard error response for unauthorized requests (401). Contains message, statusCode fields. |
|
||||
| `422` | Unprocessable Entity | Standard error response for unprocessable entity (422). Contains message, statusCode, errors array fields. |
|
||||
|
||||
### ⚠️ Special Notes & Quirks
|
||||
|
||||
> ⚠️ **User-Agent Required:** All GHL API requests require a `User-Agent` HTTP header. Requests without it may be rejected with a 403 error.
|
||||
|
||||
---
|
||||
|
||||
## POST /forms/upload-custom-files
|
||||
|
||||
**Summary:** Upload files to custom fields
|
||||
|
||||
Post the necessary fields for the API to upload files. The files need to be a buffer with the key "< custom_field_id >_< file_id >". <br /> Here custom field id is the ID of your custom field and file id is a randomly generated id (or uuid) <br /> There is support for multiple file uploads as well. Have multiple fields in the format mentioned.<br />File size is limited to 50 MB.<br /><br /> The allowed file types are: <br/> <ul><li>PDF</li><li>DOCX</li><li>DOC</li><li>JPG</li><li>JPEG</li><li>PNG</li><li>GIF</li><li>CSV</li><li>XLSX</li><li>XLS</li><li>MP4</li><li>MPEG</li><li>ZIP</li><li>RAR</li><li>TXT</li><li>SVG</li></ul> <br /><br /> The API will return the updated contact object.
|
||||
|
||||
**Version Header:** `2021-07-28`
|
||||
|
||||
**Operation ID:** `upload-to-custom-fields`
|
||||
|
||||
**Tags:** Forms
|
||||
|
||||
### cURL
|
||||
|
||||
```bash
|
||||
curl -X POST 'https://rest.gohighlevel.com/v1/forms/upload-custom-files?contactId=VALUE&locationId=VALUE' \
|
||||
-H 'Authorization: Bearer YOUR_API_TOKEN' \
|
||||
-H 'Version: 2021-07-28' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-H 'Accept: application/json' \
|
||||
-H 'User-Agent: YourApp/1.0'
|
||||
```
|
||||
|
||||
### Python (http.client)
|
||||
|
||||
```python
|
||||
import http.client
|
||||
import json
|
||||
|
||||
conn = http.client.HTTPSConnection('rest.gohighlevel.com')
|
||||
|
||||
headers = {
|
||||
'Authorization': 'Bearer YOUR_API_TOKEN',
|
||||
'Version': '2021-07-28',
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json',
|
||||
'User-Agent': 'YourApp/1.0',
|
||||
}
|
||||
|
||||
conn.request('POST', '/v1/forms/upload-custom-files?contactId=VALUE&locationId=VALUE', headers=headers)
|
||||
|
||||
response = conn.getresponse()
|
||||
data = json.loads(response.read().decode())
|
||||
print(json.dumps(data, indent=2))
|
||||
conn.close()
|
||||
```
|
||||
|
||||
### n8n HTTP Node
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "GHL - POST upload-custom-files",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"parameters": {
|
||||
"method": "POST",
|
||||
"url": "https://rest.gohighlevel.com/v1/forms/upload-custom-files",
|
||||
"authentication": "genericCredentialType",
|
||||
"genericAuthType": "httpHeaderAuth",
|
||||
"sendHeaders": true,
|
||||
"headerParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "Authorization",
|
||||
"value": "Bearer YOUR_API_TOKEN"
|
||||
},
|
||||
{
|
||||
"name": "Version",
|
||||
"value": "2021-07-28"
|
||||
},
|
||||
{
|
||||
"name": "Content-Type",
|
||||
"value": "application/json"
|
||||
},
|
||||
{
|
||||
"name": "Accept",
|
||||
"value": "application/json"
|
||||
},
|
||||
{
|
||||
"name": "User-Agent",
|
||||
"value": "YourApp/1.0"
|
||||
}
|
||||
]
|
||||
},
|
||||
"sendBody": true,
|
||||
"bodyParameters": {
|
||||
"parameters": []
|
||||
},
|
||||
"options": {},
|
||||
"sendQuery": true,
|
||||
"queryParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "contactId",
|
||||
"value": "VALUE"
|
||||
},
|
||||
{
|
||||
"name": "locationId",
|
||||
"value": "VALUE"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**To use in n8n:**
|
||||
1. Add an HTTP Request node
|
||||
2. Switch to JSON mode (Parameters → use RAW JSON)
|
||||
3. Paste the JSON above
|
||||
4. Update `YOUR_API_TOKEN` with your actual GHL API key
|
||||
|
||||
### Request Parameters
|
||||
|
||||
| Name | Location | Type | Required | Description |
|
||||
|------|----------|------|----------|-------------|
|
||||
| `contactId` | Query | `string` | ✅ | Contact ID to upload the file to. |
|
||||
| `locationId` | Query | `string` | ✅ | Location ID of the contact. |
|
||||
|
||||
### Response Codes
|
||||
|
||||
| Code | Description | Schema |
|
||||
|------|-------------|--------|
|
||||
| `200` | Successful response | — |
|
||||
| `400` | Bad Request | Standard error response for bad requests (400). Contains message, statusCode fields. |
|
||||
| `401` | Unauthorized | Standard error response for unauthorized requests (401). Contains message, statusCode fields. |
|
||||
|
||||
### ⚠️ Special Notes & Quirks
|
||||
|
||||
> ⚠️ **User-Agent Required:** All GHL API requests require a `User-Agent` HTTP header. Requests without it may be rejected with a 403 error.
|
||||
|
||||
---
|
||||
|
||||
|
||||
*Documentation generated from GHL OpenAPI specifications. Verify against official docs for latest changes.*
|
||||
@@ -0,0 +1,954 @@
|
||||
# GHL Funnels API
|
||||
|
||||
**Endpoints:** 7
|
||||
**Base URL:** `https://rest.gohighlevel.com/v1`
|
||||
|
||||
**Version Header(s):** `2021-07-28`
|
||||
|
||||
---
|
||||
|
||||
## Endpoints
|
||||
|
||||
- [GET /funnels/funnel/list](#get--funnels-funnel-list) — Fetch List of Funnels
|
||||
- [POST /funnels/lookup/redirect](#post--funnels-lookup-redirect) — Create Redirect
|
||||
- [GET /funnels/lookup/redirect/list](#get--funnels-lookup-redirect-list) — Fetch List of Redirects
|
||||
- [DELETE /funnels/lookup/redirect/{id}](#delete--funnels-lookup-redirect-id) — Delete Redirect By Id
|
||||
- [PATCH /funnels/lookup/redirect/{id}](#patch--funnels-lookup-redirect-id) — Update Redirect By Id
|
||||
- [GET /funnels/page](#get--funnels-page) — Fetch list of funnel pages
|
||||
- [GET /funnels/page/count](#get--funnels-page-count) — Fetch count of funnel pages
|
||||
|
||||
---
|
||||
|
||||
## GET /funnels/funnel/list
|
||||
|
||||
**Summary:** Fetch List of Funnels
|
||||
|
||||
Retrieves a list of all funnels based on the given query parameters.
|
||||
|
||||
**Version Header:** Not required
|
||||
|
||||
**Operation ID:** `getFunnels`
|
||||
|
||||
**Tags:** Funnel
|
||||
|
||||
### cURL
|
||||
|
||||
```bash
|
||||
curl -X GET 'https://rest.gohighlevel.com/v1/funnels/funnel/list?locationId=VALUE&type=VALUE&category=VALUE&offset=VALUE&limit=VALUE&parentId=VALUE&name=VALUE' \
|
||||
-H 'Authorization: Bearer YOUR_API_TOKEN' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-H 'Accept: application/json' \
|
||||
-H 'User-Agent: YourApp/1.0'
|
||||
```
|
||||
|
||||
### Python (http.client)
|
||||
|
||||
```python
|
||||
import http.client
|
||||
import json
|
||||
|
||||
conn = http.client.HTTPSConnection('rest.gohighlevel.com')
|
||||
|
||||
headers = {
|
||||
'Authorization': 'Bearer YOUR_API_TOKEN',
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json',
|
||||
'User-Agent': 'YourApp/1.0',
|
||||
}
|
||||
|
||||
conn.request('GET', '/v1/funnels/funnel/list?locationId=VALUE&type=VALUE&category=VALUE&offset=VALUE&limit=VALUE&parentId=VALUE&name=VALUE', headers=headers)
|
||||
|
||||
response = conn.getresponse()
|
||||
data = json.loads(response.read().decode())
|
||||
print(json.dumps(data, indent=2))
|
||||
conn.close()
|
||||
```
|
||||
|
||||
### n8n HTTP Node
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "GHL - GET list",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"parameters": {
|
||||
"method": "GET",
|
||||
"url": "https://rest.gohighlevel.com/v1/funnels/funnel/list",
|
||||
"authentication": "genericCredentialType",
|
||||
"genericAuthType": "httpHeaderAuth",
|
||||
"sendHeaders": true,
|
||||
"headerParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "Authorization",
|
||||
"value": "Bearer YOUR_API_TOKEN"
|
||||
},
|
||||
{
|
||||
"name": "Content-Type",
|
||||
"value": "application/json"
|
||||
},
|
||||
{
|
||||
"name": "Accept",
|
||||
"value": "application/json"
|
||||
},
|
||||
{
|
||||
"name": "User-Agent",
|
||||
"value": "YourApp/1.0"
|
||||
}
|
||||
]
|
||||
},
|
||||
"sendBody": false,
|
||||
"bodyParameters": {
|
||||
"parameters": []
|
||||
},
|
||||
"options": {},
|
||||
"sendQuery": true,
|
||||
"queryParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "locationId",
|
||||
"value": "VALUE"
|
||||
},
|
||||
{
|
||||
"name": "type",
|
||||
"value": "VALUE"
|
||||
},
|
||||
{
|
||||
"name": "category",
|
||||
"value": "VALUE"
|
||||
},
|
||||
{
|
||||
"name": "offset",
|
||||
"value": "VALUE"
|
||||
},
|
||||
{
|
||||
"name": "limit",
|
||||
"value": "VALUE"
|
||||
},
|
||||
{
|
||||
"name": "parentId",
|
||||
"value": "VALUE"
|
||||
},
|
||||
{
|
||||
"name": "name",
|
||||
"value": "VALUE"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**To use in n8n:**
|
||||
1. Add an HTTP Request node
|
||||
2. Switch to JSON mode (Parameters → use RAW JSON)
|
||||
3. Paste the JSON above
|
||||
4. Update `YOUR_API_TOKEN` with your actual GHL API key
|
||||
|
||||
### Request Parameters
|
||||
|
||||
| Name | Location | Type | Required | Description |
|
||||
|------|----------|------|----------|-------------|
|
||||
| `locationId` | Query | `string` | ✅ | |
|
||||
| `type` | Query | `string` | — | |
|
||||
| `category` | Query | `string` | — | |
|
||||
| `offset` | Query | `string` | — | |
|
||||
| `limit` | Query | `string` | — | |
|
||||
| `parentId` | Query | `string` | — | |
|
||||
| `name` | Query | `string` | — | |
|
||||
|
||||
### Response Codes
|
||||
|
||||
| Code | Description | Schema |
|
||||
|------|-------------|--------|
|
||||
| `200` | Successful response - List of funnels returned | - **funnels** (object) (required) (e.g. `{'_id': 'SkIDfu0S4m3NYQyvWHC6', 'dateAdded': '2024-04-29T15:00:05.681Z', 'dateUpdated': '2024-04-29T15:0... |
|
||||
|
||||
### ⚠️ Special Notes & Quirks
|
||||
|
||||
> ⚠️ **User-Agent Required:** All GHL API requests require a `User-Agent` HTTP header. Requests without it may be rejected with a 403 error.
|
||||
|
||||
---
|
||||
|
||||
## POST /funnels/lookup/redirect
|
||||
|
||||
**Summary:** Create Redirect
|
||||
|
||||
The "Create Redirect" API Allows adding a new url redirect to the system. Use this endpoint to create a url redirect with the specified details. Ensure that the required information is provided in the request payload.
|
||||
|
||||
**Version Header:** `2021-07-28`
|
||||
|
||||
**Operation ID:** `create-redirect`
|
||||
|
||||
**Tags:** Redirect
|
||||
|
||||
### cURL
|
||||
|
||||
```bash
|
||||
curl -X POST 'https://rest.gohighlevel.com/v1/funnels/lookup/redirect' \
|
||||
-H 'Authorization: Bearer YOUR_API_TOKEN' \
|
||||
-H 'Version: 2021-07-28' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-H 'Accept: application/json' \
|
||||
-H 'User-Agent: YourApp/1.0' \
|
||||
-d '{}' # See request body schema below
|
||||
```
|
||||
|
||||
### Python (http.client)
|
||||
|
||||
```python
|
||||
import http.client
|
||||
import json
|
||||
|
||||
conn = http.client.HTTPSConnection('rest.gohighlevel.com')
|
||||
|
||||
headers = {
|
||||
'Authorization': 'Bearer YOUR_API_TOKEN',
|
||||
'Version': '2021-07-28',
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json',
|
||||
'User-Agent': 'YourApp/1.0',
|
||||
}
|
||||
|
||||
payload = json.dumps({})
|
||||
conn.request('POST', '/v1/funnels/lookup/redirect', body=payload, headers=headers)
|
||||
|
||||
response = conn.getresponse()
|
||||
data = json.loads(response.read().decode())
|
||||
print(json.dumps(data, indent=2))
|
||||
conn.close()
|
||||
```
|
||||
|
||||
### n8n HTTP Node
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "GHL - POST redirect",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"parameters": {
|
||||
"method": "POST",
|
||||
"url": "https://rest.gohighlevel.com/v1/funnels/lookup/redirect",
|
||||
"authentication": "genericCredentialType",
|
||||
"genericAuthType": "httpHeaderAuth",
|
||||
"sendHeaders": true,
|
||||
"headerParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "Authorization",
|
||||
"value": "Bearer YOUR_API_TOKEN"
|
||||
},
|
||||
{
|
||||
"name": "Version",
|
||||
"value": "2021-07-28"
|
||||
},
|
||||
{
|
||||
"name": "Content-Type",
|
||||
"value": "application/json"
|
||||
},
|
||||
{
|
||||
"name": "Accept",
|
||||
"value": "application/json"
|
||||
},
|
||||
{
|
||||
"name": "User-Agent",
|
||||
"value": "YourApp/1.0"
|
||||
}
|
||||
]
|
||||
},
|
||||
"sendBody": true,
|
||||
"bodyParameters": {
|
||||
"parameters": []
|
||||
},
|
||||
"options": {}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**To use in n8n:**
|
||||
1. Add an HTTP Request node
|
||||
2. Switch to JSON mode (Parameters → use RAW JSON)
|
||||
3. Paste the JSON above
|
||||
4. Update `YOUR_API_TOKEN` with your actual GHL API key
|
||||
|
||||
### Request Body Schema
|
||||
|
||||
- **locationId** (string) (required) (e.g. `6p2RxpgtMKQwO3E6IUaT`)
|
||||
- **domain** (string) (required) (e.g. `example.com`)
|
||||
- **path** (string) (required) (e.g. `/Hello`)
|
||||
- **target** (string) (required) (e.g. `https://www.google.com`)
|
||||
- **action** (string) (required) (e.g. `URL`)
|
||||
|
||||
### Response Codes
|
||||
|
||||
| Code | Description | Schema |
|
||||
|------|-------------|--------|
|
||||
| `200` | Successful response | - **data** (string) (required): Data containing details of the created redirect |
|
||||
| `422` | Unprocessable Entity | Standard error response for unprocessable entity (422). Contains message, statusCode, errors array fields. |
|
||||
|
||||
### ⚠️ Special Notes & Quirks
|
||||
|
||||
> ⚠️ **User-Agent Required:** All GHL API requests require a `User-Agent` HTTP header. Requests without it may be rejected with a 403 error.
|
||||
|
||||
---
|
||||
|
||||
## GET /funnels/lookup/redirect/list
|
||||
|
||||
**Summary:** Fetch List of Redirects
|
||||
|
||||
Retrieves a list of all URL redirects based on the given query parameters.
|
||||
|
||||
**Version Header:** `2021-07-28`
|
||||
|
||||
**Operation ID:** `fetch-redirects-list`
|
||||
|
||||
**Tags:** Redirect
|
||||
|
||||
### cURL
|
||||
|
||||
```bash
|
||||
curl -X GET 'https://rest.gohighlevel.com/v1/funnels/lookup/redirect/list?locationId=VALUE&limit=VALUE&offset=VALUE&search=VALUE' \
|
||||
-H 'Authorization: Bearer YOUR_API_TOKEN' \
|
||||
-H 'Version: 2021-07-28' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-H 'Accept: application/json' \
|
||||
-H 'User-Agent: YourApp/1.0'
|
||||
```
|
||||
|
||||
### Python (http.client)
|
||||
|
||||
```python
|
||||
import http.client
|
||||
import json
|
||||
|
||||
conn = http.client.HTTPSConnection('rest.gohighlevel.com')
|
||||
|
||||
headers = {
|
||||
'Authorization': 'Bearer YOUR_API_TOKEN',
|
||||
'Version': '2021-07-28',
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json',
|
||||
'User-Agent': 'YourApp/1.0',
|
||||
}
|
||||
|
||||
conn.request('GET', '/v1/funnels/lookup/redirect/list?locationId=VALUE&limit=VALUE&offset=VALUE&search=VALUE', headers=headers)
|
||||
|
||||
response = conn.getresponse()
|
||||
data = json.loads(response.read().decode())
|
||||
print(json.dumps(data, indent=2))
|
||||
conn.close()
|
||||
```
|
||||
|
||||
### n8n HTTP Node
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "GHL - GET list",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"parameters": {
|
||||
"method": "GET",
|
||||
"url": "https://rest.gohighlevel.com/v1/funnels/lookup/redirect/list",
|
||||
"authentication": "genericCredentialType",
|
||||
"genericAuthType": "httpHeaderAuth",
|
||||
"sendHeaders": true,
|
||||
"headerParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "Authorization",
|
||||
"value": "Bearer YOUR_API_TOKEN"
|
||||
},
|
||||
{
|
||||
"name": "Version",
|
||||
"value": "2021-07-28"
|
||||
},
|
||||
{
|
||||
"name": "Content-Type",
|
||||
"value": "application/json"
|
||||
},
|
||||
{
|
||||
"name": "Accept",
|
||||
"value": "application/json"
|
||||
},
|
||||
{
|
||||
"name": "User-Agent",
|
||||
"value": "YourApp/1.0"
|
||||
}
|
||||
]
|
||||
},
|
||||
"sendBody": false,
|
||||
"bodyParameters": {
|
||||
"parameters": []
|
||||
},
|
||||
"options": {},
|
||||
"sendQuery": true,
|
||||
"queryParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "locationId",
|
||||
"value": "VALUE"
|
||||
},
|
||||
{
|
||||
"name": "limit",
|
||||
"value": "VALUE"
|
||||
},
|
||||
{
|
||||
"name": "offset",
|
||||
"value": "VALUE"
|
||||
},
|
||||
{
|
||||
"name": "search",
|
||||
"value": "VALUE"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**To use in n8n:**
|
||||
1. Add an HTTP Request node
|
||||
2. Switch to JSON mode (Parameters → use RAW JSON)
|
||||
3. Paste the JSON above
|
||||
4. Update `YOUR_API_TOKEN` with your actual GHL API key
|
||||
|
||||
### Request Parameters
|
||||
|
||||
| Name | Location | Type | Required | Description |
|
||||
|------|----------|------|----------|-------------|
|
||||
| `locationId` | Query | `string` | ✅ | |
|
||||
| `limit` | Query | `number` | ✅ | |
|
||||
| `offset` | Query | `number` | ✅ | |
|
||||
| `search` | Query | `string` | — | |
|
||||
|
||||
### Response Codes
|
||||
|
||||
| Code | Description | Schema |
|
||||
|------|-------------|--------|
|
||||
| `200` | Successful response - List of URL redirects returned | - **data** (object) (required) (e.g. `{'count': 42, 'data': []}`): Object containing the count of redirects and an array of redirect data |
|
||||
| `422` | Unprocessable Entity - The provided data is invalid or incomplete | — |
|
||||
|
||||
### ⚠️ Special Notes & Quirks
|
||||
|
||||
> ⚠️ **User-Agent Required:** All GHL API requests require a `User-Agent` HTTP header. Requests without it may be rejected with a 403 error.
|
||||
|
||||
---
|
||||
|
||||
## DELETE /funnels/lookup/redirect/{id}
|
||||
|
||||
**Summary:** Delete Redirect By Id
|
||||
|
||||
The "Delete Redirect By Id" API Allows deletion of a URL redirect from the system using its unique identifier. Use this endpoint to delete a URL redirect with the specified ID using details provided in the request payload.
|
||||
|
||||
**Version Header:** `2021-07-28`
|
||||
|
||||
**Operation ID:** `delete-redirect-by-id`
|
||||
|
||||
**Tags:** Redirect
|
||||
|
||||
### cURL
|
||||
|
||||
```bash
|
||||
curl -X DELETE 'https://rest.gohighlevel.com/v1/funnels/lookup/redirect/VALUE?locationId=VALUE' \
|
||||
-H 'Authorization: Bearer YOUR_API_TOKEN' \
|
||||
-H 'Version: 2021-07-28' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-H 'Accept: application/json' \
|
||||
-H 'User-Agent: YourApp/1.0'
|
||||
```
|
||||
|
||||
### Python (http.client)
|
||||
|
||||
```python
|
||||
import http.client
|
||||
import json
|
||||
|
||||
conn = http.client.HTTPSConnection('rest.gohighlevel.com')
|
||||
|
||||
headers = {
|
||||
'Authorization': 'Bearer YOUR_API_TOKEN',
|
||||
'Version': '2021-07-28',
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json',
|
||||
'User-Agent': 'YourApp/1.0',
|
||||
}
|
||||
|
||||
conn.request('DELETE', '/v1/funnels/lookup/redirect/VALUE?locationId=VALUE', headers=headers)
|
||||
|
||||
response = conn.getresponse()
|
||||
data = json.loads(response.read().decode())
|
||||
print(json.dumps(data, indent=2))
|
||||
conn.close()
|
||||
```
|
||||
|
||||
### n8n HTTP Node
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "GHL - DELETE VALUE",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"parameters": {
|
||||
"method": "DELETE",
|
||||
"url": "https://rest.gohighlevel.com/v1/funnels/lookup/redirect/VALUE",
|
||||
"authentication": "genericCredentialType",
|
||||
"genericAuthType": "httpHeaderAuth",
|
||||
"sendHeaders": true,
|
||||
"headerParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "Authorization",
|
||||
"value": "Bearer YOUR_API_TOKEN"
|
||||
},
|
||||
{
|
||||
"name": "Version",
|
||||
"value": "2021-07-28"
|
||||
},
|
||||
{
|
||||
"name": "Content-Type",
|
||||
"value": "application/json"
|
||||
},
|
||||
{
|
||||
"name": "Accept",
|
||||
"value": "application/json"
|
||||
},
|
||||
{
|
||||
"name": "User-Agent",
|
||||
"value": "YourApp/1.0"
|
||||
}
|
||||
]
|
||||
},
|
||||
"sendBody": false,
|
||||
"bodyParameters": {
|
||||
"parameters": []
|
||||
},
|
||||
"options": {},
|
||||
"sendQuery": true,
|
||||
"queryParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "locationId",
|
||||
"value": "VALUE"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**To use in n8n:**
|
||||
1. Add an HTTP Request node
|
||||
2. Switch to JSON mode (Parameters → use RAW JSON)
|
||||
3. Paste the JSON above
|
||||
4. Update `YOUR_API_TOKEN` with your actual GHL API key
|
||||
|
||||
### Request Parameters
|
||||
|
||||
| Name | Location | Type | Required | Description |
|
||||
|------|----------|------|----------|-------------|
|
||||
| `id` | Path | `string` | ✅ | |
|
||||
| `locationId` | Query | `string` | ✅ | |
|
||||
|
||||
### Response Codes
|
||||
|
||||
| Code | Description | Schema |
|
||||
|------|-------------|--------|
|
||||
| `200` | Successful response - URL redirect deleted successfully | - **data** (object) (required) (e.g. `{'status': 'ok'}`): Status of the delete operation |
|
||||
| `422` | Unprocessable Entity - The provided data is invalid or incomplete | — |
|
||||
|
||||
### ⚠️ Special Notes & Quirks
|
||||
|
||||
> ⚠️ **User-Agent Required:** All GHL API requests require a `User-Agent` HTTP header. Requests without it may be rejected with a 403 error.
|
||||
|
||||
---
|
||||
|
||||
## PATCH /funnels/lookup/redirect/{id}
|
||||
|
||||
**Summary:** Update Redirect By Id
|
||||
|
||||
The "Update Redirect By Id" API Allows updating an existing URL redirect in the system. Use this endpoint to modify a URL redirect with the specified ID using details provided in the request payload.
|
||||
|
||||
**Version Header:** `2021-07-28`
|
||||
|
||||
**Operation ID:** `update-redirect-by-id`
|
||||
|
||||
**Tags:** Redirect
|
||||
|
||||
### cURL
|
||||
|
||||
```bash
|
||||
curl -X PATCH 'https://rest.gohighlevel.com/v1/funnels/lookup/redirect/VALUE' \
|
||||
-H 'Authorization: Bearer YOUR_API_TOKEN' \
|
||||
-H 'Version: 2021-07-28' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-H 'Accept: application/json' \
|
||||
-H 'User-Agent: YourApp/1.0' \
|
||||
-d '{}' # See request body schema below
|
||||
```
|
||||
|
||||
### Python (http.client)
|
||||
|
||||
```python
|
||||
import http.client
|
||||
import json
|
||||
|
||||
conn = http.client.HTTPSConnection('rest.gohighlevel.com')
|
||||
|
||||
headers = {
|
||||
'Authorization': 'Bearer YOUR_API_TOKEN',
|
||||
'Version': '2021-07-28',
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json',
|
||||
'User-Agent': 'YourApp/1.0',
|
||||
}
|
||||
|
||||
payload = json.dumps({})
|
||||
conn.request('PATCH', '/v1/funnels/lookup/redirect/VALUE', body=payload, headers=headers)
|
||||
|
||||
response = conn.getresponse()
|
||||
data = json.loads(response.read().decode())
|
||||
print(json.dumps(data, indent=2))
|
||||
conn.close()
|
||||
```
|
||||
|
||||
### n8n HTTP Node
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "GHL - PATCH VALUE",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"parameters": {
|
||||
"method": "PATCH",
|
||||
"url": "https://rest.gohighlevel.com/v1/funnels/lookup/redirect/VALUE",
|
||||
"authentication": "genericCredentialType",
|
||||
"genericAuthType": "httpHeaderAuth",
|
||||
"sendHeaders": true,
|
||||
"headerParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "Authorization",
|
||||
"value": "Bearer YOUR_API_TOKEN"
|
||||
},
|
||||
{
|
||||
"name": "Version",
|
||||
"value": "2021-07-28"
|
||||
},
|
||||
{
|
||||
"name": "Content-Type",
|
||||
"value": "application/json"
|
||||
},
|
||||
{
|
||||
"name": "Accept",
|
||||
"value": "application/json"
|
||||
},
|
||||
{
|
||||
"name": "User-Agent",
|
||||
"value": "YourApp/1.0"
|
||||
}
|
||||
]
|
||||
},
|
||||
"sendBody": true,
|
||||
"bodyParameters": {
|
||||
"parameters": []
|
||||
},
|
||||
"options": {}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**To use in n8n:**
|
||||
1. Add an HTTP Request node
|
||||
2. Switch to JSON mode (Parameters → use RAW JSON)
|
||||
3. Paste the JSON above
|
||||
4. Update `YOUR_API_TOKEN` with your actual GHL API key
|
||||
|
||||
### Request Parameters
|
||||
|
||||
| Name | Location | Type | Required | Description |
|
||||
|------|----------|------|----------|-------------|
|
||||
| `id` | Path | `string` | ✅ | |
|
||||
|
||||
### Request Body Schema
|
||||
|
||||
- **target** (string) (required) (e.g. `https://www.google.com`)
|
||||
- **action** (string) (required) (e.g. `URL`)
|
||||
- **locationId** (string) (required) (e.g. `6p2RxpgtMKQwO3E6IUaT`)
|
||||
|
||||
### Response Codes
|
||||
|
||||
| Code | Description | Schema |
|
||||
|------|-------------|--------|
|
||||
| `200` | Successful response | - **data** (string) (required): Data containing details of the updated redirect |
|
||||
| `422` | Unprocessable Entity | Standard error response for unprocessable entity (422). Contains message, statusCode, errors array fields. |
|
||||
|
||||
### ⚠️ Special Notes & Quirks
|
||||
|
||||
> ⚠️ **User-Agent Required:** All GHL API requests require a `User-Agent` HTTP header. Requests without it may be rejected with a 403 error.
|
||||
|
||||
---
|
||||
|
||||
## GET /funnels/page
|
||||
|
||||
**Summary:** Fetch list of funnel pages
|
||||
|
||||
Retrieves a list of all funnel pages based on the given query parameters.
|
||||
|
||||
**Version Header:** Not required
|
||||
|
||||
**Operation ID:** `getPagesByFunnelId`
|
||||
|
||||
**Tags:** Funnel
|
||||
|
||||
### cURL
|
||||
|
||||
```bash
|
||||
curl -X GET 'https://rest.gohighlevel.com/v1/funnels/page?locationId=VALUE&funnelId=VALUE&name=VALUE&limit=VALUE&offset=VALUE' \
|
||||
-H 'Authorization: Bearer YOUR_API_TOKEN' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-H 'Accept: application/json' \
|
||||
-H 'User-Agent: YourApp/1.0'
|
||||
```
|
||||
|
||||
### Python (http.client)
|
||||
|
||||
```python
|
||||
import http.client
|
||||
import json
|
||||
|
||||
conn = http.client.HTTPSConnection('rest.gohighlevel.com')
|
||||
|
||||
headers = {
|
||||
'Authorization': 'Bearer YOUR_API_TOKEN',
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json',
|
||||
'User-Agent': 'YourApp/1.0',
|
||||
}
|
||||
|
||||
conn.request('GET', '/v1/funnels/page?locationId=VALUE&funnelId=VALUE&name=VALUE&limit=VALUE&offset=VALUE', headers=headers)
|
||||
|
||||
response = conn.getresponse()
|
||||
data = json.loads(response.read().decode())
|
||||
print(json.dumps(data, indent=2))
|
||||
conn.close()
|
||||
```
|
||||
|
||||
### n8n HTTP Node
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "GHL - GET page",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"parameters": {
|
||||
"method": "GET",
|
||||
"url": "https://rest.gohighlevel.com/v1/funnels/page",
|
||||
"authentication": "genericCredentialType",
|
||||
"genericAuthType": "httpHeaderAuth",
|
||||
"sendHeaders": true,
|
||||
"headerParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "Authorization",
|
||||
"value": "Bearer YOUR_API_TOKEN"
|
||||
},
|
||||
{
|
||||
"name": "Content-Type",
|
||||
"value": "application/json"
|
||||
},
|
||||
{
|
||||
"name": "Accept",
|
||||
"value": "application/json"
|
||||
},
|
||||
{
|
||||
"name": "User-Agent",
|
||||
"value": "YourApp/1.0"
|
||||
}
|
||||
]
|
||||
},
|
||||
"sendBody": false,
|
||||
"bodyParameters": {
|
||||
"parameters": []
|
||||
},
|
||||
"options": {},
|
||||
"sendQuery": true,
|
||||
"queryParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "locationId",
|
||||
"value": "VALUE"
|
||||
},
|
||||
{
|
||||
"name": "funnelId",
|
||||
"value": "VALUE"
|
||||
},
|
||||
{
|
||||
"name": "name",
|
||||
"value": "VALUE"
|
||||
},
|
||||
{
|
||||
"name": "limit",
|
||||
"value": "VALUE"
|
||||
},
|
||||
{
|
||||
"name": "offset",
|
||||
"value": "VALUE"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**To use in n8n:**
|
||||
1. Add an HTTP Request node
|
||||
2. Switch to JSON mode (Parameters → use RAW JSON)
|
||||
3. Paste the JSON above
|
||||
4. Update `YOUR_API_TOKEN` with your actual GHL API key
|
||||
|
||||
### Request Parameters
|
||||
|
||||
| Name | Location | Type | Required | Description |
|
||||
|------|----------|------|----------|-------------|
|
||||
| `locationId` | Query | `string` | ✅ | |
|
||||
| `funnelId` | Query | `string` | ✅ | |
|
||||
| `name` | Query | `string` | — | |
|
||||
| `limit` | Query | `number` | ✅ | |
|
||||
| `offset` | Query | `number` | ✅ | |
|
||||
|
||||
### Response Codes
|
||||
|
||||
| Code | Description | Schema |
|
||||
|------|-------------|--------|
|
||||
| `200` | Successful response - List of funnel pages returned | - **_id** (string) (required) (e.g. `0yJbP3q7t7pLmeTWRAE2`)<br> - **locationId** (string) (required) (e.g. `ojQjykmwNIU88vfsfzvH`)<br> - **funnelId**... |
|
||||
|
||||
### ⚠️ Special Notes & Quirks
|
||||
|
||||
> ⚠️ **User-Agent Required:** All GHL API requests require a `User-Agent` HTTP header. Requests without it may be rejected with a 403 error.
|
||||
|
||||
---
|
||||
|
||||
## GET /funnels/page/count
|
||||
|
||||
**Summary:** Fetch count of funnel pages
|
||||
|
||||
Retrieves count of all funnel pages based on the given query parameters.
|
||||
|
||||
**Version Header:** Not required
|
||||
|
||||
**Operation ID:** `getPagesCountByFunnelId`
|
||||
|
||||
**Tags:** Funnel
|
||||
|
||||
### cURL
|
||||
|
||||
```bash
|
||||
curl -X GET 'https://rest.gohighlevel.com/v1/funnels/page/count?locationId=VALUE&funnelId=VALUE&name=VALUE' \
|
||||
-H 'Authorization: Bearer YOUR_API_TOKEN' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-H 'Accept: application/json' \
|
||||
-H 'User-Agent: YourApp/1.0'
|
||||
```
|
||||
|
||||
### Python (http.client)
|
||||
|
||||
```python
|
||||
import http.client
|
||||
import json
|
||||
|
||||
conn = http.client.HTTPSConnection('rest.gohighlevel.com')
|
||||
|
||||
headers = {
|
||||
'Authorization': 'Bearer YOUR_API_TOKEN',
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json',
|
||||
'User-Agent': 'YourApp/1.0',
|
||||
}
|
||||
|
||||
conn.request('GET', '/v1/funnels/page/count?locationId=VALUE&funnelId=VALUE&name=VALUE', headers=headers)
|
||||
|
||||
response = conn.getresponse()
|
||||
data = json.loads(response.read().decode())
|
||||
print(json.dumps(data, indent=2))
|
||||
conn.close()
|
||||
```
|
||||
|
||||
### n8n HTTP Node
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "GHL - GET count",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"parameters": {
|
||||
"method": "GET",
|
||||
"url": "https://rest.gohighlevel.com/v1/funnels/page/count",
|
||||
"authentication": "genericCredentialType",
|
||||
"genericAuthType": "httpHeaderAuth",
|
||||
"sendHeaders": true,
|
||||
"headerParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "Authorization",
|
||||
"value": "Bearer YOUR_API_TOKEN"
|
||||
},
|
||||
{
|
||||
"name": "Content-Type",
|
||||
"value": "application/json"
|
||||
},
|
||||
{
|
||||
"name": "Accept",
|
||||
"value": "application/json"
|
||||
},
|
||||
{
|
||||
"name": "User-Agent",
|
||||
"value": "YourApp/1.0"
|
||||
}
|
||||
]
|
||||
},
|
||||
"sendBody": false,
|
||||
"bodyParameters": {
|
||||
"parameters": []
|
||||
},
|
||||
"options": {},
|
||||
"sendQuery": true,
|
||||
"queryParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "locationId",
|
||||
"value": "VALUE"
|
||||
},
|
||||
{
|
||||
"name": "funnelId",
|
||||
"value": "VALUE"
|
||||
},
|
||||
{
|
||||
"name": "name",
|
||||
"value": "VALUE"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**To use in n8n:**
|
||||
1. Add an HTTP Request node
|
||||
2. Switch to JSON mode (Parameters → use RAW JSON)
|
||||
3. Paste the JSON above
|
||||
4. Update `YOUR_API_TOKEN` with your actual GHL API key
|
||||
|
||||
### Request Parameters
|
||||
|
||||
| Name | Location | Type | Required | Description |
|
||||
|------|----------|------|----------|-------------|
|
||||
| `locationId` | Query | `string` | ✅ | |
|
||||
| `funnelId` | Query | `string` | ✅ | |
|
||||
| `name` | Query | `string` | — | |
|
||||
|
||||
### Response Codes
|
||||
|
||||
| Code | Description | Schema |
|
||||
|------|-------------|--------|
|
||||
| `200` | Successful response - Count of funnel pages returned | - **count** (number) (required) (e.g. `20`) |
|
||||
|
||||
### ⚠️ Special Notes & Quirks
|
||||
|
||||
> ⚠️ **User-Agent Required:** All GHL API requests require a `User-Agent` HTTP header. Requests without it may be rejected with a 403 error.
|
||||
|
||||
---
|
||||
|
||||
|
||||
*Documentation generated from GHL OpenAPI specifications. Verify against official docs for latest changes.*
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,789 @@
|
||||
# GHL Links API
|
||||
|
||||
> ⚠️ **Mixed Versions:** Most Links endpoints use `2021-07-28`, but `GET /links/search` uses `Version: 2021-04-15`.
|
||||
|
||||
**Endpoints:** 6
|
||||
**Base URL:** `https://rest.gohighlevel.com/v1`
|
||||
|
||||
**Version Header(s):** `2021-04-15, 2021-07-28`
|
||||
|
||||
---
|
||||
|
||||
## Endpoints
|
||||
|
||||
- [GET /links/](#get--links-) — Get Links
|
||||
- [POST /links/](#post--links-) — Create Link
|
||||
- [GET /links/id/{linkId}](#get--links-id-linkId) — Get Link by ID
|
||||
- [GET /links/search](#get--links-search) — Search Trigger Links
|
||||
- [DELETE /links/{linkId}](#delete--links-linkId) — Delete Link
|
||||
- [PUT /links/{linkId}](#put--links-linkId) — Update Link
|
||||
|
||||
---
|
||||
|
||||
## GET /links/
|
||||
|
||||
**Summary:** Get Links
|
||||
|
||||
Get Links
|
||||
|
||||
**Version Header:** `2021-07-28`
|
||||
|
||||
**Operation ID:** `get-links`
|
||||
|
||||
**Tags:** Trigger Links
|
||||
|
||||
### cURL
|
||||
|
||||
```bash
|
||||
curl -X GET 'https://rest.gohighlevel.com/v1/links/?locationId=ve9EPM428h8vShlRW1KT' \
|
||||
-H 'Authorization: Bearer YOUR_API_TOKEN' \
|
||||
-H 'Version: 2021-07-28' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-H 'Accept: application/json' \
|
||||
-H 'User-Agent: YourApp/1.0'
|
||||
```
|
||||
|
||||
### Python (http.client)
|
||||
|
||||
```python
|
||||
import http.client
|
||||
import json
|
||||
|
||||
conn = http.client.HTTPSConnection('rest.gohighlevel.com')
|
||||
|
||||
headers = {
|
||||
'Authorization': 'Bearer YOUR_API_TOKEN',
|
||||
'Version': '2021-07-28',
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json',
|
||||
'User-Agent': 'YourApp/1.0',
|
||||
}
|
||||
|
||||
conn.request('GET', '/v1/links/?locationId=ve9EPM428h8vShlRW1KT', headers=headers)
|
||||
|
||||
response = conn.getresponse()
|
||||
data = json.loads(response.read().decode())
|
||||
print(json.dumps(data, indent=2))
|
||||
conn.close()
|
||||
```
|
||||
|
||||
### n8n HTTP Node
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "GHL - GET links",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"parameters": {
|
||||
"method": "GET",
|
||||
"url": "https://rest.gohighlevel.com/v1/links/",
|
||||
"authentication": "genericCredentialType",
|
||||
"genericAuthType": "httpHeaderAuth",
|
||||
"sendHeaders": true,
|
||||
"headerParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "Authorization",
|
||||
"value": "Bearer YOUR_API_TOKEN"
|
||||
},
|
||||
{
|
||||
"name": "Version",
|
||||
"value": "2021-07-28"
|
||||
},
|
||||
{
|
||||
"name": "Content-Type",
|
||||
"value": "application/json"
|
||||
},
|
||||
{
|
||||
"name": "Accept",
|
||||
"value": "application/json"
|
||||
},
|
||||
{
|
||||
"name": "User-Agent",
|
||||
"value": "YourApp/1.0"
|
||||
}
|
||||
]
|
||||
},
|
||||
"sendBody": false,
|
||||
"bodyParameters": {
|
||||
"parameters": []
|
||||
},
|
||||
"options": {},
|
||||
"sendQuery": true,
|
||||
"queryParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "locationId",
|
||||
"value": "ve9EPM428h8vShlRW1KT"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**To use in n8n:**
|
||||
1. Add an HTTP Request node
|
||||
2. Switch to JSON mode (Parameters → use RAW JSON)
|
||||
3. Paste the JSON above
|
||||
4. Update `YOUR_API_TOKEN` with your actual GHL API key
|
||||
|
||||
### Request Parameters
|
||||
|
||||
| Name | Location | Type | Required | Description |
|
||||
|------|----------|------|----------|-------------|
|
||||
| `locationId` | Query | `string` | ✅ | [example: `ve9EPM428h8vShlRW1KT`] |
|
||||
|
||||
### Response Codes
|
||||
|
||||
| Code | Description | Schema |
|
||||
|------|-------------|--------|
|
||||
| `200` | Successful response | - **links** (array of LinkSchema) |
|
||||
| `400` | Bad Request | Standard error response for bad requests (400). Contains message, statusCode fields. |
|
||||
| `401` | Unauthorized | Standard error response for unauthorized requests (401). Contains message, statusCode fields. |
|
||||
|
||||
### ⚠️ Special Notes & Quirks
|
||||
|
||||
> ⚠️ **User-Agent Required:** All GHL API requests require a `User-Agent` HTTP header. Requests without it may be rejected with a 403 error.
|
||||
|
||||
---
|
||||
|
||||
## POST /links/
|
||||
|
||||
**Summary:** Create Link
|
||||
|
||||
Create Link
|
||||
|
||||
**Version Header:** `2021-07-28`
|
||||
|
||||
**Operation ID:** `create-link`
|
||||
|
||||
**Tags:** Trigger Links
|
||||
|
||||
### cURL
|
||||
|
||||
```bash
|
||||
curl -X POST 'https://rest.gohighlevel.com/v1/links/' \
|
||||
-H 'Authorization: Bearer YOUR_API_TOKEN' \
|
||||
-H 'Version: 2021-07-28' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-H 'Accept: application/json' \
|
||||
-H 'User-Agent: YourApp/1.0' \
|
||||
-d '{}' # See request body schema below
|
||||
```
|
||||
|
||||
### Python (http.client)
|
||||
|
||||
```python
|
||||
import http.client
|
||||
import json
|
||||
|
||||
conn = http.client.HTTPSConnection('rest.gohighlevel.com')
|
||||
|
||||
headers = {
|
||||
'Authorization': 'Bearer YOUR_API_TOKEN',
|
||||
'Version': '2021-07-28',
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json',
|
||||
'User-Agent': 'YourApp/1.0',
|
||||
}
|
||||
|
||||
payload = json.dumps({})
|
||||
conn.request('POST', '/v1/links/', body=payload, headers=headers)
|
||||
|
||||
response = conn.getresponse()
|
||||
data = json.loads(response.read().decode())
|
||||
print(json.dumps(data, indent=2))
|
||||
conn.close()
|
||||
```
|
||||
|
||||
### n8n HTTP Node
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "GHL - POST links",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"parameters": {
|
||||
"method": "POST",
|
||||
"url": "https://rest.gohighlevel.com/v1/links/",
|
||||
"authentication": "genericCredentialType",
|
||||
"genericAuthType": "httpHeaderAuth",
|
||||
"sendHeaders": true,
|
||||
"headerParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "Authorization",
|
||||
"value": "Bearer YOUR_API_TOKEN"
|
||||
},
|
||||
{
|
||||
"name": "Version",
|
||||
"value": "2021-07-28"
|
||||
},
|
||||
{
|
||||
"name": "Content-Type",
|
||||
"value": "application/json"
|
||||
},
|
||||
{
|
||||
"name": "Accept",
|
||||
"value": "application/json"
|
||||
},
|
||||
{
|
||||
"name": "User-Agent",
|
||||
"value": "YourApp/1.0"
|
||||
}
|
||||
]
|
||||
},
|
||||
"sendBody": true,
|
||||
"bodyParameters": {
|
||||
"parameters": []
|
||||
},
|
||||
"options": {}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**To use in n8n:**
|
||||
1. Add an HTTP Request node
|
||||
2. Switch to JSON mode (Parameters → use RAW JSON)
|
||||
3. Paste the JSON above
|
||||
4. Update `YOUR_API_TOKEN` with your actual GHL API key
|
||||
|
||||
### Request Body Schema
|
||||
|
||||
- **locationId** (string) (required) (e.g. `ve9EPM428h8vShlRW1KT`)
|
||||
- **name** (string) (required) (e.g. `first tag`)
|
||||
- **redirectTo** (string) (required) (e.g. `https://www.google.com/`)
|
||||
|
||||
### Response Codes
|
||||
|
||||
| Code | Description | Schema |
|
||||
|------|-------------|--------|
|
||||
| `201` | Successful response | - **link** (LinkSchema) |
|
||||
| `400` | Bad Request | Standard error response for bad requests (400). Contains message, statusCode fields. |
|
||||
| `401` | Unauthorized | Standard error response for unauthorized requests (401). Contains message, statusCode fields. |
|
||||
| `422` | Unprocessable Entity | Standard error response for unprocessable entity (422). Contains message, statusCode, errors array fields. |
|
||||
|
||||
### ⚠️ Special Notes & Quirks
|
||||
|
||||
> ⚠️ **User-Agent Required:** All GHL API requests require a `User-Agent` HTTP header. Requests without it may be rejected with a 403 error.
|
||||
|
||||
---
|
||||
|
||||
## GET /links/id/{linkId}
|
||||
|
||||
**Summary:** Get Link by ID
|
||||
|
||||
Get a single link by its ID
|
||||
|
||||
**Version Header:** `2021-07-28`
|
||||
|
||||
**Operation ID:** `get-link-by-id`
|
||||
|
||||
**Tags:** Trigger Links
|
||||
|
||||
### cURL
|
||||
|
||||
```bash
|
||||
curl -X GET 'https://rest.gohighlevel.com/v1/links/id/VALUE?locationId=ABCHkzuJQ8ZMd4Te84GK' \
|
||||
-H 'Authorization: Bearer YOUR_API_TOKEN' \
|
||||
-H 'Version: 2021-07-28' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-H 'Accept: application/json' \
|
||||
-H 'User-Agent: YourApp/1.0'
|
||||
```
|
||||
|
||||
### Python (http.client)
|
||||
|
||||
```python
|
||||
import http.client
|
||||
import json
|
||||
|
||||
conn = http.client.HTTPSConnection('rest.gohighlevel.com')
|
||||
|
||||
headers = {
|
||||
'Authorization': 'Bearer YOUR_API_TOKEN',
|
||||
'Version': '2021-07-28',
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json',
|
||||
'User-Agent': 'YourApp/1.0',
|
||||
}
|
||||
|
||||
conn.request('GET', '/v1/links/id/VALUE?locationId=ABCHkzuJQ8ZMd4Te84GK', headers=headers)
|
||||
|
||||
response = conn.getresponse()
|
||||
data = json.loads(response.read().decode())
|
||||
print(json.dumps(data, indent=2))
|
||||
conn.close()
|
||||
```
|
||||
|
||||
### n8n HTTP Node
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "GHL - GET VALUE",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"parameters": {
|
||||
"method": "GET",
|
||||
"url": "https://rest.gohighlevel.com/v1/links/id/VALUE",
|
||||
"authentication": "genericCredentialType",
|
||||
"genericAuthType": "httpHeaderAuth",
|
||||
"sendHeaders": true,
|
||||
"headerParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "Authorization",
|
||||
"value": "Bearer YOUR_API_TOKEN"
|
||||
},
|
||||
{
|
||||
"name": "Version",
|
||||
"value": "2021-07-28"
|
||||
},
|
||||
{
|
||||
"name": "Content-Type",
|
||||
"value": "application/json"
|
||||
},
|
||||
{
|
||||
"name": "Accept",
|
||||
"value": "application/json"
|
||||
},
|
||||
{
|
||||
"name": "User-Agent",
|
||||
"value": "YourApp/1.0"
|
||||
}
|
||||
]
|
||||
},
|
||||
"sendBody": false,
|
||||
"bodyParameters": {
|
||||
"parameters": []
|
||||
},
|
||||
"options": {},
|
||||
"sendQuery": true,
|
||||
"queryParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "locationId",
|
||||
"value": "ABCHkzuJQ8ZMd4Te84GK"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**To use in n8n:**
|
||||
1. Add an HTTP Request node
|
||||
2. Switch to JSON mode (Parameters → use RAW JSON)
|
||||
3. Paste the JSON above
|
||||
4. Update `YOUR_API_TOKEN` with your actual GHL API key
|
||||
|
||||
### Request Parameters
|
||||
|
||||
| Name | Location | Type | Required | Description |
|
||||
|------|----------|------|----------|-------------|
|
||||
| `Authorization` | Header | `string` | ✅ | Access Token [example: `Bearer 9c48df2694a849b6089f9d0d3513efe`] |
|
||||
| `locationId` | Query | `string` | ✅ | Location Id [example: `ABCHkzuJQ8ZMd4Te84GK`] |
|
||||
| `linkId` | Path | `string` | ✅ | Link Id |
|
||||
|
||||
### Response Codes
|
||||
|
||||
| Code | Description | Schema |
|
||||
|------|-------------|--------|
|
||||
| `200` | Successful response | - **link** (LinkSchema) |
|
||||
| `400` | Bad Request | (ref: #/components/schemas/BadRequestDTO) |
|
||||
| `401` | Unauthorized | (ref: #/components/schemas/UnauthorizedDTO) |
|
||||
|
||||
### ⚠️ Special Notes & Quirks
|
||||
|
||||
> ⚠️ **User-Agent Required:** All GHL API requests require a `User-Agent` HTTP header. Requests without it may be rejected with a 403 error.
|
||||
|
||||
---
|
||||
|
||||
## GET /links/search
|
||||
|
||||
**Summary:** Search Trigger Links
|
||||
|
||||
Get list of links by searching
|
||||
|
||||
**Version Header:** `2021-04-15`
|
||||
|
||||
**Operation ID:** `search-trigger-links`
|
||||
|
||||
**Tags:** Trigger Links Search
|
||||
|
||||
### cURL
|
||||
|
||||
```bash
|
||||
curl -X GET 'https://rest.gohighlevel.com/v1/links/search?locationId=ABCHkzuJQ8ZMd4Te84GK&query=Search string&skip=1&limit=10' \
|
||||
-H 'Authorization: Bearer YOUR_API_TOKEN' \
|
||||
-H 'Version: 2021-04-15' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-H 'Accept: application/json' \
|
||||
-H 'User-Agent: YourApp/1.0'
|
||||
```
|
||||
|
||||
### Python (http.client)
|
||||
|
||||
```python
|
||||
import http.client
|
||||
import json
|
||||
|
||||
conn = http.client.HTTPSConnection('rest.gohighlevel.com')
|
||||
|
||||
headers = {
|
||||
'Authorization': 'Bearer YOUR_API_TOKEN',
|
||||
'Version': '2021-04-15',
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json',
|
||||
'User-Agent': 'YourApp/1.0',
|
||||
}
|
||||
|
||||
conn.request('GET', '/v1/links/search?locationId=ABCHkzuJQ8ZMd4Te84GK&query=Search string&skip=1&limit=10', headers=headers)
|
||||
|
||||
response = conn.getresponse()
|
||||
data = json.loads(response.read().decode())
|
||||
print(json.dumps(data, indent=2))
|
||||
conn.close()
|
||||
```
|
||||
|
||||
### n8n HTTP Node
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "GHL - GET search",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"parameters": {
|
||||
"method": "GET",
|
||||
"url": "https://rest.gohighlevel.com/v1/links/search",
|
||||
"authentication": "genericCredentialType",
|
||||
"genericAuthType": "httpHeaderAuth",
|
||||
"sendHeaders": true,
|
||||
"headerParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "Authorization",
|
||||
"value": "Bearer YOUR_API_TOKEN"
|
||||
},
|
||||
{
|
||||
"name": "Version",
|
||||
"value": "2021-04-15"
|
||||
},
|
||||
{
|
||||
"name": "Content-Type",
|
||||
"value": "application/json"
|
||||
},
|
||||
{
|
||||
"name": "Accept",
|
||||
"value": "application/json"
|
||||
},
|
||||
{
|
||||
"name": "User-Agent",
|
||||
"value": "YourApp/1.0"
|
||||
}
|
||||
]
|
||||
},
|
||||
"sendBody": false,
|
||||
"bodyParameters": {
|
||||
"parameters": []
|
||||
},
|
||||
"options": {},
|
||||
"sendQuery": true,
|
||||
"queryParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "locationId",
|
||||
"value": "ABCHkzuJQ8ZMd4Te84GK"
|
||||
},
|
||||
{
|
||||
"name": "query",
|
||||
"value": "Search string"
|
||||
},
|
||||
{
|
||||
"name": "skip",
|
||||
"value": 1
|
||||
},
|
||||
{
|
||||
"name": "limit",
|
||||
"value": 10
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**To use in n8n:**
|
||||
1. Add an HTTP Request node
|
||||
2. Switch to JSON mode (Parameters → use RAW JSON)
|
||||
3. Paste the JSON above
|
||||
4. Update `YOUR_API_TOKEN` with your actual GHL API key
|
||||
|
||||
### Request Parameters
|
||||
|
||||
| Name | Location | Type | Required | Description |
|
||||
|------|----------|------|----------|-------------|
|
||||
| `Authorization` | Header | `string` | ✅ | Access Token [example: `Bearer 9c48df2694a849b6089f9d0d3513efe`] |
|
||||
| `locationId` | Query | `string` | ✅ | Location Id [example: `ABCHkzuJQ8ZMd4Te84GK`] |
|
||||
| `query` | Query | `string` | — | Search query as a string [example: `Search string`] |
|
||||
| `skip` | Query | `number` | — | Numbers of query results to skip [example: `1`] |
|
||||
| `limit` | Query | `number` | — | Limit on number of search results [example: `10`] |
|
||||
|
||||
### Response Codes
|
||||
|
||||
| Code | Description | Schema |
|
||||
|------|-------------|--------|
|
||||
| `200` | Successful response | - **links** (array of LinkSchema) |
|
||||
| `400` | Bad Request | (ref: #/components/schemas/BadRequestDTO) |
|
||||
| `401` | Unauthorized | (ref: #/components/schemas/UnauthorizedDTO) |
|
||||
|
||||
### ⚠️ Special Notes & Quirks
|
||||
|
||||
> ⚠️ **User-Agent Required:** All GHL API requests require a `User-Agent` HTTP header. Requests without it may be rejected with a 403 error.
|
||||
|
||||
---
|
||||
|
||||
## DELETE /links/{linkId}
|
||||
|
||||
**Summary:** Delete Link
|
||||
|
||||
Delete Link
|
||||
|
||||
**Version Header:** `2021-07-28`
|
||||
|
||||
**Operation ID:** `delete-link`
|
||||
|
||||
**Tags:** Trigger Links
|
||||
|
||||
### cURL
|
||||
|
||||
```bash
|
||||
curl -X DELETE 'https://rest.gohighlevel.com/v1/links/VALUE' \
|
||||
-H 'Authorization: Bearer YOUR_API_TOKEN' \
|
||||
-H 'Version: 2021-07-28' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-H 'Accept: application/json' \
|
||||
-H 'User-Agent: YourApp/1.0'
|
||||
```
|
||||
|
||||
### Python (http.client)
|
||||
|
||||
```python
|
||||
import http.client
|
||||
import json
|
||||
|
||||
conn = http.client.HTTPSConnection('rest.gohighlevel.com')
|
||||
|
||||
headers = {
|
||||
'Authorization': 'Bearer YOUR_API_TOKEN',
|
||||
'Version': '2021-07-28',
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json',
|
||||
'User-Agent': 'YourApp/1.0',
|
||||
}
|
||||
|
||||
conn.request('DELETE', '/v1/links/VALUE', headers=headers)
|
||||
|
||||
response = conn.getresponse()
|
||||
data = json.loads(response.read().decode())
|
||||
print(json.dumps(data, indent=2))
|
||||
conn.close()
|
||||
```
|
||||
|
||||
### n8n HTTP Node
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "GHL - DELETE VALUE",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"parameters": {
|
||||
"method": "DELETE",
|
||||
"url": "https://rest.gohighlevel.com/v1/links/VALUE",
|
||||
"authentication": "genericCredentialType",
|
||||
"genericAuthType": "httpHeaderAuth",
|
||||
"sendHeaders": true,
|
||||
"headerParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "Authorization",
|
||||
"value": "Bearer YOUR_API_TOKEN"
|
||||
},
|
||||
{
|
||||
"name": "Version",
|
||||
"value": "2021-07-28"
|
||||
},
|
||||
{
|
||||
"name": "Content-Type",
|
||||
"value": "application/json"
|
||||
},
|
||||
{
|
||||
"name": "Accept",
|
||||
"value": "application/json"
|
||||
},
|
||||
{
|
||||
"name": "User-Agent",
|
||||
"value": "YourApp/1.0"
|
||||
}
|
||||
]
|
||||
},
|
||||
"sendBody": false,
|
||||
"bodyParameters": {
|
||||
"parameters": []
|
||||
},
|
||||
"options": {}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**To use in n8n:**
|
||||
1. Add an HTTP Request node
|
||||
2. Switch to JSON mode (Parameters → use RAW JSON)
|
||||
3. Paste the JSON above
|
||||
4. Update `YOUR_API_TOKEN` with your actual GHL API key
|
||||
|
||||
### Request Parameters
|
||||
|
||||
| Name | Location | Type | Required | Description |
|
||||
|------|----------|------|----------|-------------|
|
||||
| `linkId` | Path | `string` | ✅ | Link Id |
|
||||
|
||||
### Response Codes
|
||||
|
||||
| Code | Description | Schema |
|
||||
|------|-------------|--------|
|
||||
| `201` | Successful response | - **succeded** (boolean) (e.g. `True`) |
|
||||
| `400` | Bad Request | Standard error response for bad requests (400). Contains message, statusCode fields. |
|
||||
| `401` | Unauthorized | Standard error response for unauthorized requests (401). Contains message, statusCode fields. |
|
||||
| `422` | Unprocessable Entity | Standard error response for unprocessable entity (422). Contains message, statusCode, errors array fields. |
|
||||
|
||||
### ⚠️ Special Notes & Quirks
|
||||
|
||||
> ⚠️ **User-Agent Required:** All GHL API requests require a `User-Agent` HTTP header. Requests without it may be rejected with a 403 error.
|
||||
|
||||
---
|
||||
|
||||
## PUT /links/{linkId}
|
||||
|
||||
**Summary:** Update Link
|
||||
|
||||
Update Link
|
||||
|
||||
**Version Header:** `2021-07-28`
|
||||
|
||||
**Operation ID:** `update-link`
|
||||
|
||||
**Tags:** Trigger Links
|
||||
|
||||
### cURL
|
||||
|
||||
```bash
|
||||
curl -X PUT 'https://rest.gohighlevel.com/v1/links/VALUE' \
|
||||
-H 'Authorization: Bearer YOUR_API_TOKEN' \
|
||||
-H 'Version: 2021-07-28' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-H 'Accept: application/json' \
|
||||
-H 'User-Agent: YourApp/1.0' \
|
||||
-d '{}' # See request body schema below
|
||||
```
|
||||
|
||||
### Python (http.client)
|
||||
|
||||
```python
|
||||
import http.client
|
||||
import json
|
||||
|
||||
conn = http.client.HTTPSConnection('rest.gohighlevel.com')
|
||||
|
||||
headers = {
|
||||
'Authorization': 'Bearer YOUR_API_TOKEN',
|
||||
'Version': '2021-07-28',
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json',
|
||||
'User-Agent': 'YourApp/1.0',
|
||||
}
|
||||
|
||||
payload = json.dumps({})
|
||||
conn.request('PUT', '/v1/links/VALUE', body=payload, headers=headers)
|
||||
|
||||
response = conn.getresponse()
|
||||
data = json.loads(response.read().decode())
|
||||
print(json.dumps(data, indent=2))
|
||||
conn.close()
|
||||
```
|
||||
|
||||
### n8n HTTP Node
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "GHL - PUT VALUE",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"parameters": {
|
||||
"method": "PUT",
|
||||
"url": "https://rest.gohighlevel.com/v1/links/VALUE",
|
||||
"authentication": "genericCredentialType",
|
||||
"genericAuthType": "httpHeaderAuth",
|
||||
"sendHeaders": true,
|
||||
"headerParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "Authorization",
|
||||
"value": "Bearer YOUR_API_TOKEN"
|
||||
},
|
||||
{
|
||||
"name": "Version",
|
||||
"value": "2021-07-28"
|
||||
},
|
||||
{
|
||||
"name": "Content-Type",
|
||||
"value": "application/json"
|
||||
},
|
||||
{
|
||||
"name": "Accept",
|
||||
"value": "application/json"
|
||||
},
|
||||
{
|
||||
"name": "User-Agent",
|
||||
"value": "YourApp/1.0"
|
||||
}
|
||||
]
|
||||
},
|
||||
"sendBody": true,
|
||||
"bodyParameters": {
|
||||
"parameters": []
|
||||
},
|
||||
"options": {}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**To use in n8n:**
|
||||
1. Add an HTTP Request node
|
||||
2. Switch to JSON mode (Parameters → use RAW JSON)
|
||||
3. Paste the JSON above
|
||||
4. Update `YOUR_API_TOKEN` with your actual GHL API key
|
||||
|
||||
### Request Parameters
|
||||
|
||||
| Name | Location | Type | Required | Description |
|
||||
|------|----------|------|----------|-------------|
|
||||
| `linkId` | Path | `string` | ✅ | Link Id |
|
||||
|
||||
### Request Body Schema
|
||||
|
||||
- **name** (string) (required) (e.g. `first tag`)
|
||||
- **redirectTo** (string) (required) (e.g. `https://www.google.com/`)
|
||||
|
||||
### Response Codes
|
||||
|
||||
| Code | Description | Schema |
|
||||
|------|-------------|--------|
|
||||
| `201` | Successful response | - **link** (LinkSchema) |
|
||||
| `400` | Bad Request | Standard error response for bad requests (400). Contains message, statusCode fields. |
|
||||
| `401` | Unauthorized | Standard error response for unauthorized requests (401). Contains message, statusCode fields. |
|
||||
| `422` | Unprocessable Entity | Standard error response for unprocessable entity (422). Contains message, statusCode, errors array fields. |
|
||||
|
||||
### ⚠️ Special Notes & Quirks
|
||||
|
||||
> ⚠️ **User-Agent Required:** All GHL API requests require a `User-Agent` HTTP header. Requests without it may be rejected with a 403 error.
|
||||
|
||||
---
|
||||
|
||||
|
||||
*Documentation generated from GHL OpenAPI specifications. Verify against official docs for latest changes.*
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,914 @@
|
||||
# GHL Media API
|
||||
|
||||
**Endpoints:** 7
|
||||
**Base URL:** `https://rest.gohighlevel.com/v1`
|
||||
|
||||
**Version Header(s):** `2021-07-28`
|
||||
|
||||
---
|
||||
|
||||
## Endpoints
|
||||
|
||||
- [PUT /medias/delete-files](#put--medias-delete-files) — Bulk Delete / Trash Files or Folders
|
||||
- [GET /medias/files](#get--medias-files) — Get List of Files/ Folders
|
||||
- [POST /medias/folder](#post--medias-folder) — Create Folder
|
||||
- [PUT /medias/update-files](#put--medias-update-files) — Bulk Update Files/ Folders
|
||||
- [POST /medias/upload-file](#post--medias-upload-file) — Upload File into Media Storage
|
||||
- [DELETE /medias/{id}](#delete--medias-id) — Delete File or Folder
|
||||
- [POST /medias/{id}](#post--medias-id) — Update File/ Folder
|
||||
|
||||
---
|
||||
|
||||
## PUT /medias/delete-files
|
||||
|
||||
**Summary:** Bulk Delete / Trash Files or Folders
|
||||
|
||||
Soft-deletes or trashes multiple files and folders in a single request
|
||||
|
||||
**Version Header:** `2021-07-28`
|
||||
|
||||
**Operation ID:** `bulk-delete-media-objects`
|
||||
|
||||
**Tags:** Medias
|
||||
|
||||
### cURL
|
||||
|
||||
```bash
|
||||
curl -X PUT 'https://rest.gohighlevel.com/v1/medias/delete-files' \
|
||||
-H 'Authorization: Bearer YOUR_API_TOKEN' \
|
||||
-H 'Version: 2021-07-28' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-H 'Accept: application/json' \
|
||||
-H 'User-Agent: YourApp/1.0' \
|
||||
-d '{}' # See request body schema below
|
||||
```
|
||||
|
||||
### Python (http.client)
|
||||
|
||||
```python
|
||||
import http.client
|
||||
import json
|
||||
|
||||
conn = http.client.HTTPSConnection('rest.gohighlevel.com')
|
||||
|
||||
headers = {
|
||||
'Authorization': 'Bearer YOUR_API_TOKEN',
|
||||
'Version': '2021-07-28',
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json',
|
||||
'User-Agent': 'YourApp/1.0',
|
||||
}
|
||||
|
||||
payload = json.dumps({})
|
||||
conn.request('PUT', '/v1/medias/delete-files', body=payload, headers=headers)
|
||||
|
||||
response = conn.getresponse()
|
||||
data = json.loads(response.read().decode())
|
||||
print(json.dumps(data, indent=2))
|
||||
conn.close()
|
||||
```
|
||||
|
||||
### n8n HTTP Node
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "GHL - PUT delete-files",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"parameters": {
|
||||
"method": "PUT",
|
||||
"url": "https://rest.gohighlevel.com/v1/medias/delete-files",
|
||||
"authentication": "genericCredentialType",
|
||||
"genericAuthType": "httpHeaderAuth",
|
||||
"sendHeaders": true,
|
||||
"headerParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "Authorization",
|
||||
"value": "Bearer YOUR_API_TOKEN"
|
||||
},
|
||||
{
|
||||
"name": "Version",
|
||||
"value": "2021-07-28"
|
||||
},
|
||||
{
|
||||
"name": "Content-Type",
|
||||
"value": "application/json"
|
||||
},
|
||||
{
|
||||
"name": "Accept",
|
||||
"value": "application/json"
|
||||
},
|
||||
{
|
||||
"name": "User-Agent",
|
||||
"value": "YourApp/1.0"
|
||||
}
|
||||
]
|
||||
},
|
||||
"sendBody": true,
|
||||
"bodyParameters": {
|
||||
"parameters": []
|
||||
},
|
||||
"options": {}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**To use in n8n:**
|
||||
1. Add an HTTP Request node
|
||||
2. Switch to JSON mode (Parameters → use RAW JSON)
|
||||
3. Paste the JSON above
|
||||
4. Update `YOUR_API_TOKEN` with your actual GHL API key
|
||||
|
||||
### Request Body Schema
|
||||
|
||||
- **filesToBeDeleted** (array of DeleteMediaObjectItem) (required) (e.g. `[{'_id': '686f630df0d3166d68fbcec2'}]`): Array of file objects to be deleted or trashed
|
||||
- **altType** (string) (required) (e.g. `location`): Type of entity that owns the files
|
||||
- **altId** (string) (required) (e.g. `sx6wyHhbFdRXh302LLNR`): Location identifier
|
||||
- **status** (string) (required) (e.g. `deleted`): Status to set for the files (deleted or trashed)
|
||||
|
||||
### Response Codes
|
||||
|
||||
| Code | Description | Schema |
|
||||
|------|-------------|--------|
|
||||
| `200` | Successful response | object — no documented properties |
|
||||
|
||||
### ⚠️ Special Notes & Quirks
|
||||
|
||||
> ⚠️ **User-Agent Required:** All GHL API requests require a `User-Agent` HTTP header. Requests without it may be rejected with a 403 error.
|
||||
|
||||
---
|
||||
|
||||
## GET /medias/files
|
||||
|
||||
**Summary:** Get List of Files/ Folders
|
||||
|
||||
Fetches list of files and folders from the media storage
|
||||
|
||||
**Version Header:** `2021-07-28`
|
||||
|
||||
**Operation ID:** `fetch-media-content`
|
||||
|
||||
**Tags:** Medias
|
||||
|
||||
### cURL
|
||||
|
||||
```bash
|
||||
curl -X GET 'https://rest.gohighlevel.com/v1/medias/files?offset=VALUE&limit=VALUE&sortBy=VALUE&sortOrder=VALUE&type=VALUE&query=VALUE&altType=location&altId=VALUE&parentId=VALUE&fetchAll=VALUE' \
|
||||
-H 'Authorization: Bearer YOUR_API_TOKEN' \
|
||||
-H 'Version: 2021-07-28' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-H 'Accept: application/json' \
|
||||
-H 'User-Agent: YourApp/1.0'
|
||||
```
|
||||
|
||||
### Python (http.client)
|
||||
|
||||
```python
|
||||
import http.client
|
||||
import json
|
||||
|
||||
conn = http.client.HTTPSConnection('rest.gohighlevel.com')
|
||||
|
||||
headers = {
|
||||
'Authorization': 'Bearer YOUR_API_TOKEN',
|
||||
'Version': '2021-07-28',
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json',
|
||||
'User-Agent': 'YourApp/1.0',
|
||||
}
|
||||
|
||||
conn.request('GET', '/v1/medias/files?offset=VALUE&limit=VALUE&sortBy=VALUE&sortOrder=VALUE&type=VALUE&query=VALUE&altType=VALUE&altId=VALUE&parentId=VALUE&fetchAll=VALUE', headers=headers)
|
||||
|
||||
response = conn.getresponse()
|
||||
data = json.loads(response.read().decode())
|
||||
print(json.dumps(data, indent=2))
|
||||
conn.close()
|
||||
```
|
||||
|
||||
### n8n HTTP Node
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "GHL - GET files",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"parameters": {
|
||||
"method": "GET",
|
||||
"url": "https://rest.gohighlevel.com/v1/medias/files",
|
||||
"authentication": "genericCredentialType",
|
||||
"genericAuthType": "httpHeaderAuth",
|
||||
"sendHeaders": true,
|
||||
"headerParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "Authorization",
|
||||
"value": "Bearer YOUR_API_TOKEN"
|
||||
},
|
||||
{
|
||||
"name": "Version",
|
||||
"value": "2021-07-28"
|
||||
},
|
||||
{
|
||||
"name": "Content-Type",
|
||||
"value": "application/json"
|
||||
},
|
||||
{
|
||||
"name": "Accept",
|
||||
"value": "application/json"
|
||||
},
|
||||
{
|
||||
"name": "User-Agent",
|
||||
"value": "YourApp/1.0"
|
||||
}
|
||||
]
|
||||
},
|
||||
"sendBody": false,
|
||||
"bodyParameters": {
|
||||
"parameters": []
|
||||
},
|
||||
"options": {},
|
||||
"sendQuery": true,
|
||||
"queryParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "offset",
|
||||
"value": "VALUE"
|
||||
},
|
||||
{
|
||||
"name": "limit",
|
||||
"value": "VALUE"
|
||||
},
|
||||
{
|
||||
"name": "sortBy",
|
||||
"value": "VALUE"
|
||||
},
|
||||
{
|
||||
"name": "sortOrder",
|
||||
"value": "VALUE"
|
||||
},
|
||||
{
|
||||
"name": "type",
|
||||
"value": "VALUE"
|
||||
},
|
||||
{
|
||||
"name": "query",
|
||||
"value": "VALUE"
|
||||
},
|
||||
{
|
||||
"name": "altType",
|
||||
"value": "VALUE"
|
||||
},
|
||||
{
|
||||
"name": "altId",
|
||||
"value": "VALUE"
|
||||
},
|
||||
{
|
||||
"name": "parentId",
|
||||
"value": "VALUE"
|
||||
},
|
||||
{
|
||||
"name": "fetchAll",
|
||||
"value": "VALUE"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**To use in n8n:**
|
||||
1. Add an HTTP Request node
|
||||
2. Switch to JSON mode (Parameters → use RAW JSON)
|
||||
3. Paste the JSON above
|
||||
4. Update `YOUR_API_TOKEN` with your actual GHL API key
|
||||
|
||||
### Request Parameters
|
||||
|
||||
| Name | Location | Type | Required | Description |
|
||||
|------|----------|------|----------|-------------|
|
||||
| `offset` | Query | `string` | — | Number of files to skip in listing |
|
||||
| `limit` | Query | `string` | — | Number of files to show in the listing |
|
||||
| `sortBy` | Query | `string` | ✅ | Field to sorting the file listing by |
|
||||
| `sortOrder` | Query | `string` | ✅ | Direction in which file needs to be sorted |
|
||||
| `type` | Query | `string` | ✅ | Type |
|
||||
| `query` | Query | `string` | — | Query text |
|
||||
| `altType` | Query | `string` | ✅ | AltType (values: location) |
|
||||
| `altId` | Query | `string` | ✅ | location Id |
|
||||
| `parentId` | Query | `string` | — | parent id or folder id |
|
||||
| `fetchAll` | Query | `string` | — | Fetch all files or folders |
|
||||
|
||||
### Response Codes
|
||||
|
||||
| Code | Description | Schema |
|
||||
|------|-------------|--------|
|
||||
| `200` | Successful response | - **files** (array) (required) (e.g. `{'altId': 'locationId', 'altType': 'location', 'name': 'file name', 'parentId': 'parent folder id', 'url': ... |
|
||||
|
||||
### ⚠️ Special Notes & Quirks
|
||||
|
||||
> ⚠️ **User-Agent Required:** All GHL API requests require a `User-Agent` HTTP header. Requests without it may be rejected with a 403 error.
|
||||
|
||||
---
|
||||
|
||||
## POST /medias/folder
|
||||
|
||||
**Summary:** Create Folder
|
||||
|
||||
Creates a new folder in the media storage
|
||||
|
||||
**Version Header:** `2021-07-28`
|
||||
|
||||
**Operation ID:** `create-media-folder`
|
||||
|
||||
**Tags:** Medias
|
||||
|
||||
### cURL
|
||||
|
||||
```bash
|
||||
curl -X POST 'https://rest.gohighlevel.com/v1/medias/folder' \
|
||||
-H 'Authorization: Bearer YOUR_API_TOKEN' \
|
||||
-H 'Version: 2021-07-28' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-H 'Accept: application/json' \
|
||||
-H 'User-Agent: YourApp/1.0' \
|
||||
-d '{}' # See request body schema below
|
||||
```
|
||||
|
||||
### Python (http.client)
|
||||
|
||||
```python
|
||||
import http.client
|
||||
import json
|
||||
|
||||
conn = http.client.HTTPSConnection('rest.gohighlevel.com')
|
||||
|
||||
headers = {
|
||||
'Authorization': 'Bearer YOUR_API_TOKEN',
|
||||
'Version': '2021-07-28',
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json',
|
||||
'User-Agent': 'YourApp/1.0',
|
||||
}
|
||||
|
||||
payload = json.dumps({})
|
||||
conn.request('POST', '/v1/medias/folder', body=payload, headers=headers)
|
||||
|
||||
response = conn.getresponse()
|
||||
data = json.loads(response.read().decode())
|
||||
print(json.dumps(data, indent=2))
|
||||
conn.close()
|
||||
```
|
||||
|
||||
### n8n HTTP Node
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "GHL - POST folder",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"parameters": {
|
||||
"method": "POST",
|
||||
"url": "https://rest.gohighlevel.com/v1/medias/folder",
|
||||
"authentication": "genericCredentialType",
|
||||
"genericAuthType": "httpHeaderAuth",
|
||||
"sendHeaders": true,
|
||||
"headerParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "Authorization",
|
||||
"value": "Bearer YOUR_API_TOKEN"
|
||||
},
|
||||
{
|
||||
"name": "Version",
|
||||
"value": "2021-07-28"
|
||||
},
|
||||
{
|
||||
"name": "Content-Type",
|
||||
"value": "application/json"
|
||||
},
|
||||
{
|
||||
"name": "Accept",
|
||||
"value": "application/json"
|
||||
},
|
||||
{
|
||||
"name": "User-Agent",
|
||||
"value": "YourApp/1.0"
|
||||
}
|
||||
]
|
||||
},
|
||||
"sendBody": true,
|
||||
"bodyParameters": {
|
||||
"parameters": []
|
||||
},
|
||||
"options": {}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**To use in n8n:**
|
||||
1. Add an HTTP Request node
|
||||
2. Switch to JSON mode (Parameters → use RAW JSON)
|
||||
3. Paste the JSON above
|
||||
4. Update `YOUR_API_TOKEN` with your actual GHL API key
|
||||
|
||||
### Request Body Schema
|
||||
|
||||
- **altId** (string) (required) (e.g. `sx6wyHhbFdRXh302LLNR`): Location Id
|
||||
- **altType** (string) (required) (e.g. `location`): Type of entity (location only)
|
||||
- **name** (string) (required) (e.g. `New Folder`): Name of the folder to be created
|
||||
- **parentId** (string) (e.g. `64af50c42d567a3b4f5989e0`): ID of the parent folder (optional)
|
||||
|
||||
### Response Codes
|
||||
|
||||
| Code | Description | Schema |
|
||||
|------|-------------|--------|
|
||||
| `200` | Returns the newly created folder object | - **altId** (string) (required) (e.g. `sx6wyHhbFdRXh302LLNR`): Location identifier that owns this folder<br> - **altType** (string) (required) (e.g... |
|
||||
|
||||
### ⚠️ Special Notes & Quirks
|
||||
|
||||
> ⚠️ **User-Agent Required:** All GHL API requests require a `User-Agent` HTTP header. Requests without it may be rejected with a 403 error.
|
||||
|
||||
---
|
||||
|
||||
## PUT /medias/update-files
|
||||
|
||||
**Summary:** Bulk Update Files/ Folders
|
||||
|
||||
Updates metadata or status of multiple files and folders
|
||||
|
||||
**Version Header:** `2021-07-28`
|
||||
|
||||
**Operation ID:** `bulk-update-media-objects`
|
||||
|
||||
**Tags:** Medias
|
||||
|
||||
### cURL
|
||||
|
||||
```bash
|
||||
curl -X PUT 'https://rest.gohighlevel.com/v1/medias/update-files' \
|
||||
-H 'Authorization: Bearer YOUR_API_TOKEN' \
|
||||
-H 'Version: 2021-07-28' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-H 'Accept: application/json' \
|
||||
-H 'User-Agent: YourApp/1.0' \
|
||||
-d '{}' # See request body schema below
|
||||
```
|
||||
|
||||
### Python (http.client)
|
||||
|
||||
```python
|
||||
import http.client
|
||||
import json
|
||||
|
||||
conn = http.client.HTTPSConnection('rest.gohighlevel.com')
|
||||
|
||||
headers = {
|
||||
'Authorization': 'Bearer YOUR_API_TOKEN',
|
||||
'Version': '2021-07-28',
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json',
|
||||
'User-Agent': 'YourApp/1.0',
|
||||
}
|
||||
|
||||
payload = json.dumps({})
|
||||
conn.request('PUT', '/v1/medias/update-files', body=payload, headers=headers)
|
||||
|
||||
response = conn.getresponse()
|
||||
data = json.loads(response.read().decode())
|
||||
print(json.dumps(data, indent=2))
|
||||
conn.close()
|
||||
```
|
||||
|
||||
### n8n HTTP Node
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "GHL - PUT update-files",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"parameters": {
|
||||
"method": "PUT",
|
||||
"url": "https://rest.gohighlevel.com/v1/medias/update-files",
|
||||
"authentication": "genericCredentialType",
|
||||
"genericAuthType": "httpHeaderAuth",
|
||||
"sendHeaders": true,
|
||||
"headerParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "Authorization",
|
||||
"value": "Bearer YOUR_API_TOKEN"
|
||||
},
|
||||
{
|
||||
"name": "Version",
|
||||
"value": "2021-07-28"
|
||||
},
|
||||
{
|
||||
"name": "Content-Type",
|
||||
"value": "application/json"
|
||||
},
|
||||
{
|
||||
"name": "Accept",
|
||||
"value": "application/json"
|
||||
},
|
||||
{
|
||||
"name": "User-Agent",
|
||||
"value": "YourApp/1.0"
|
||||
}
|
||||
]
|
||||
},
|
||||
"sendBody": true,
|
||||
"bodyParameters": {
|
||||
"parameters": []
|
||||
},
|
||||
"options": {}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**To use in n8n:**
|
||||
1. Add an HTTP Request node
|
||||
2. Switch to JSON mode (Parameters → use RAW JSON)
|
||||
3. Paste the JSON above
|
||||
4. Update `YOUR_API_TOKEN` with your actual GHL API key
|
||||
|
||||
### Request Body Schema
|
||||
|
||||
- **altId** (string) (required) (e.g. `sx6wyHhbFdRXh302LLNR`): Location identifier
|
||||
- **altType** (string) (required) (e.g. `location`): Type of entity that owns the files
|
||||
- **filesToBeUpdated** (array of UpdateMediaObject) (required) (e.g. `[{'id': '686f9817f0d3165be9fbcef6', 'name': 'Updated File Name.pdf'}]`): Array of file objects to be updated
|
||||
|
||||
### Response Codes
|
||||
|
||||
| Code | Description | Schema |
|
||||
|------|-------------|--------|
|
||||
| `200` | Successful response | object — no documented properties |
|
||||
|
||||
### ⚠️ Special Notes & Quirks
|
||||
|
||||
> ⚠️ **User-Agent Required:** All GHL API requests require a `User-Agent` HTTP header. Requests without it may be rejected with a 403 error.
|
||||
|
||||
---
|
||||
|
||||
## POST /medias/upload-file
|
||||
|
||||
**Summary:** Upload File into Media Storage
|
||||
|
||||
If hosted is set to true then fileUrl is required. Else file is required. If adding a file, maximum allowed is 25 MB
|
||||
|
||||
**Version Header:** `2021-07-28`
|
||||
|
||||
**Operation ID:** `upload-media-content`
|
||||
|
||||
**Tags:** Medias
|
||||
|
||||
### cURL
|
||||
|
||||
```bash
|
||||
curl -X POST 'https://rest.gohighlevel.com/v1/medias/upload-file' \
|
||||
-H 'Authorization: Bearer YOUR_API_TOKEN' \
|
||||
-H 'Version: 2021-07-28' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-H 'Accept: application/json' \
|
||||
-H 'User-Agent: YourApp/1.0'
|
||||
```
|
||||
|
||||
### Python (http.client)
|
||||
|
||||
```python
|
||||
import http.client
|
||||
import json
|
||||
|
||||
conn = http.client.HTTPSConnection('rest.gohighlevel.com')
|
||||
|
||||
headers = {
|
||||
'Authorization': 'Bearer YOUR_API_TOKEN',
|
||||
'Version': '2021-07-28',
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json',
|
||||
'User-Agent': 'YourApp/1.0',
|
||||
}
|
||||
|
||||
conn.request('POST', '/v1/medias/upload-file', headers=headers)
|
||||
|
||||
response = conn.getresponse()
|
||||
data = json.loads(response.read().decode())
|
||||
print(json.dumps(data, indent=2))
|
||||
conn.close()
|
||||
```
|
||||
|
||||
### n8n HTTP Node
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "GHL - POST upload-file",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"parameters": {
|
||||
"method": "POST",
|
||||
"url": "https://rest.gohighlevel.com/v1/medias/upload-file",
|
||||
"authentication": "genericCredentialType",
|
||||
"genericAuthType": "httpHeaderAuth",
|
||||
"sendHeaders": true,
|
||||
"headerParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "Authorization",
|
||||
"value": "Bearer YOUR_API_TOKEN"
|
||||
},
|
||||
{
|
||||
"name": "Version",
|
||||
"value": "2021-07-28"
|
||||
},
|
||||
{
|
||||
"name": "Content-Type",
|
||||
"value": "application/json"
|
||||
},
|
||||
{
|
||||
"name": "Accept",
|
||||
"value": "application/json"
|
||||
},
|
||||
{
|
||||
"name": "User-Agent",
|
||||
"value": "YourApp/1.0"
|
||||
}
|
||||
]
|
||||
},
|
||||
"sendBody": true,
|
||||
"bodyParameters": {
|
||||
"parameters": []
|
||||
},
|
||||
"options": {}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**To use in n8n:**
|
||||
1. Add an HTTP Request node
|
||||
2. Switch to JSON mode (Parameters → use RAW JSON)
|
||||
3. Paste the JSON above
|
||||
4. Update `YOUR_API_TOKEN` with your actual GHL API key
|
||||
|
||||
### Response Codes
|
||||
|
||||
| Code | Description | Schema |
|
||||
|------|-------------|--------|
|
||||
| `200` | Successful response | - **fileId** (string) (required) (e.g. `file.pdf`): ID of the uploaded file<br> - **url** (string) (required) (e.g. `https://storage.googleapis.com... |
|
||||
|
||||
### ⚠️ Special Notes & Quirks
|
||||
|
||||
> ⚠️ **User-Agent Required:** All GHL API requests require a `User-Agent` HTTP header. Requests without it may be rejected with a 403 error.
|
||||
|
||||
---
|
||||
|
||||
## DELETE /medias/{id}
|
||||
|
||||
**Summary:** Delete File or Folder
|
||||
|
||||
Deletes specific file or folder from the media storage
|
||||
|
||||
**Version Header:** `2021-07-28`
|
||||
|
||||
**Operation ID:** `delete-media-content`
|
||||
|
||||
**Tags:** Medias
|
||||
|
||||
### cURL
|
||||
|
||||
```bash
|
||||
curl -X DELETE 'https://rest.gohighlevel.com/v1/medias/VALUE?altType=location&altId=VALUE' \
|
||||
-H 'Authorization: Bearer YOUR_API_TOKEN' \
|
||||
-H 'Version: 2021-07-28' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-H 'Accept: application/json' \
|
||||
-H 'User-Agent: YourApp/1.0'
|
||||
```
|
||||
|
||||
### Python (http.client)
|
||||
|
||||
```python
|
||||
import http.client
|
||||
import json
|
||||
|
||||
conn = http.client.HTTPSConnection('rest.gohighlevel.com')
|
||||
|
||||
headers = {
|
||||
'Authorization': 'Bearer YOUR_API_TOKEN',
|
||||
'Version': '2021-07-28',
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json',
|
||||
'User-Agent': 'YourApp/1.0',
|
||||
}
|
||||
|
||||
conn.request('DELETE', '/v1/medias/VALUE?altType=VALUE&altId=VALUE', headers=headers)
|
||||
|
||||
response = conn.getresponse()
|
||||
data = json.loads(response.read().decode())
|
||||
print(json.dumps(data, indent=2))
|
||||
conn.close()
|
||||
```
|
||||
|
||||
### n8n HTTP Node
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "GHL - DELETE VALUE",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"parameters": {
|
||||
"method": "DELETE",
|
||||
"url": "https://rest.gohighlevel.com/v1/medias/VALUE",
|
||||
"authentication": "genericCredentialType",
|
||||
"genericAuthType": "httpHeaderAuth",
|
||||
"sendHeaders": true,
|
||||
"headerParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "Authorization",
|
||||
"value": "Bearer YOUR_API_TOKEN"
|
||||
},
|
||||
{
|
||||
"name": "Version",
|
||||
"value": "2021-07-28"
|
||||
},
|
||||
{
|
||||
"name": "Content-Type",
|
||||
"value": "application/json"
|
||||
},
|
||||
{
|
||||
"name": "Accept",
|
||||
"value": "application/json"
|
||||
},
|
||||
{
|
||||
"name": "User-Agent",
|
||||
"value": "YourApp/1.0"
|
||||
}
|
||||
]
|
||||
},
|
||||
"sendBody": false,
|
||||
"bodyParameters": {
|
||||
"parameters": []
|
||||
},
|
||||
"options": {},
|
||||
"sendQuery": true,
|
||||
"queryParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "altType",
|
||||
"value": "VALUE"
|
||||
},
|
||||
{
|
||||
"name": "altId",
|
||||
"value": "VALUE"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**To use in n8n:**
|
||||
1. Add an HTTP Request node
|
||||
2. Switch to JSON mode (Parameters → use RAW JSON)
|
||||
3. Paste the JSON above
|
||||
4. Update `YOUR_API_TOKEN` with your actual GHL API key
|
||||
|
||||
### Request Parameters
|
||||
|
||||
| Name | Location | Type | Required | Description |
|
||||
|------|----------|------|----------|-------------|
|
||||
| `id` | Path | `string` | ✅ | |
|
||||
| `altType` | Query | `string` | ✅ | AltType (values: location) |
|
||||
| `altId` | Query | `string` | ✅ | location Id |
|
||||
|
||||
### Response Codes
|
||||
|
||||
| Code | Description | Schema |
|
||||
|------|-------------|--------|
|
||||
| `200` | Successful response | — |
|
||||
|
||||
### ⚠️ Special Notes & Quirks
|
||||
|
||||
> ⚠️ **User-Agent Required:** All GHL API requests require a `User-Agent` HTTP header. Requests without it may be rejected with a 403 error.
|
||||
|
||||
---
|
||||
|
||||
## POST /medias/{id}
|
||||
|
||||
**Summary:** Update File/ Folder
|
||||
|
||||
Updates a single file or folder by ID
|
||||
|
||||
**Version Header:** `2021-07-28`
|
||||
|
||||
**Operation ID:** `update-media-object`
|
||||
|
||||
**Tags:** Medias
|
||||
|
||||
### cURL
|
||||
|
||||
```bash
|
||||
curl -X POST 'https://rest.gohighlevel.com/v1/medias/VALUE' \
|
||||
-H 'Authorization: Bearer YOUR_API_TOKEN' \
|
||||
-H 'Version: 2021-07-28' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-H 'Accept: application/json' \
|
||||
-H 'User-Agent: YourApp/1.0' \
|
||||
-d '{}' # See request body schema below
|
||||
```
|
||||
|
||||
### Python (http.client)
|
||||
|
||||
```python
|
||||
import http.client
|
||||
import json
|
||||
|
||||
conn = http.client.HTTPSConnection('rest.gohighlevel.com')
|
||||
|
||||
headers = {
|
||||
'Authorization': 'Bearer YOUR_API_TOKEN',
|
||||
'Version': '2021-07-28',
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json',
|
||||
'User-Agent': 'YourApp/1.0',
|
||||
}
|
||||
|
||||
payload = json.dumps({})
|
||||
conn.request('POST', '/v1/medias/VALUE', body=payload, headers=headers)
|
||||
|
||||
response = conn.getresponse()
|
||||
data = json.loads(response.read().decode())
|
||||
print(json.dumps(data, indent=2))
|
||||
conn.close()
|
||||
```
|
||||
|
||||
### n8n HTTP Node
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "GHL - POST VALUE",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"parameters": {
|
||||
"method": "POST",
|
||||
"url": "https://rest.gohighlevel.com/v1/medias/VALUE",
|
||||
"authentication": "genericCredentialType",
|
||||
"genericAuthType": "httpHeaderAuth",
|
||||
"sendHeaders": true,
|
||||
"headerParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "Authorization",
|
||||
"value": "Bearer YOUR_API_TOKEN"
|
||||
},
|
||||
{
|
||||
"name": "Version",
|
||||
"value": "2021-07-28"
|
||||
},
|
||||
{
|
||||
"name": "Content-Type",
|
||||
"value": "application/json"
|
||||
},
|
||||
{
|
||||
"name": "Accept",
|
||||
"value": "application/json"
|
||||
},
|
||||
{
|
||||
"name": "User-Agent",
|
||||
"value": "YourApp/1.0"
|
||||
}
|
||||
]
|
||||
},
|
||||
"sendBody": true,
|
||||
"bodyParameters": {
|
||||
"parameters": []
|
||||
},
|
||||
"options": {}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**To use in n8n:**
|
||||
1. Add an HTTP Request node
|
||||
2. Switch to JSON mode (Parameters → use RAW JSON)
|
||||
3. Paste the JSON above
|
||||
4. Update `YOUR_API_TOKEN` with your actual GHL API key
|
||||
|
||||
### Request Parameters
|
||||
|
||||
| Name | Location | Type | Required | Description |
|
||||
|------|----------|------|----------|-------------|
|
||||
| `id` | Path | `string` | ✅ | Unique identifier of the file or folder to update |
|
||||
|
||||
### Request Body Schema
|
||||
|
||||
- **name** (string) (required) (e.g. `Updated File Name.pdf`): New name for the file or folder
|
||||
- **altType** (string) (required) (e.g. `location`): Type of entity that owns the file or folder
|
||||
- **altId** (string) (required) (e.g. `sx6wyHhbFdRXh302LLNR`): Location identifier that owns the file or folder
|
||||
|
||||
### Response Codes
|
||||
|
||||
| Code | Description | Schema |
|
||||
|------|-------------|--------|
|
||||
| `200` | Successful response | object — no documented properties |
|
||||
|
||||
### ⚠️ Special Notes & Quirks
|
||||
|
||||
> ⚠️ **User-Agent Required:** All GHL API requests require a `User-Agent` HTTP header. Requests without it may be rejected with a 403 error.
|
||||
|
||||
---
|
||||
|
||||
|
||||
*Documentation generated from GHL OpenAPI specifications. Verify against official docs for latest changes.*
|
||||
@@ -0,0 +1,412 @@
|
||||
# GHL OAuth API
|
||||
|
||||
**Endpoints:** 3
|
||||
**Base URL:** `https://rest.gohighlevel.com/v1`
|
||||
|
||||
**Version Header(s):** `2021-07-28`
|
||||
|
||||
---
|
||||
|
||||
## Endpoints
|
||||
|
||||
- [GET /oauth/installedLocations](#get--oauth-installedLocations) — Get Location where app is installed
|
||||
- [POST /oauth/locationToken](#post--oauth-locationToken) — Get Location Access Token from Agency Token
|
||||
- [POST /oauth/token](#post--oauth-token) — Get Access Token
|
||||
|
||||
---
|
||||
|
||||
## GET /oauth/installedLocations
|
||||
|
||||
**Summary:** Get Location where app is installed
|
||||
|
||||
This API allows you fetch location where app is installed upon
|
||||
|
||||
**Version Header:** `2021-07-28`
|
||||
|
||||
**Operation ID:** `get-installed-location`
|
||||
|
||||
**Tags:** OAuth 2.0
|
||||
|
||||
### cURL
|
||||
|
||||
```bash
|
||||
curl -X GET 'https://rest.gohighlevel.com/v1/oauth/installedLocations?skip=1&limit=10&query=location name&isInstalled=True&companyId=tDtDnQdgm2LXpyiqYvZ6&appId=tDtDnQdgm2LXpyiqYvZ6&versionId=tDtDnQdgm2LXpyiqYvZ6&onTrial=True&planId=True&locationId=1245' \
|
||||
-H 'Authorization: Bearer YOUR_API_TOKEN' \
|
||||
-H 'Version: 2021-07-28' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-H 'Accept: application/json' \
|
||||
-H 'User-Agent: YourApp/1.0'
|
||||
```
|
||||
|
||||
### Python (http.client)
|
||||
|
||||
```python
|
||||
import http.client
|
||||
import json
|
||||
|
||||
conn = http.client.HTTPSConnection('rest.gohighlevel.com')
|
||||
|
||||
headers = {
|
||||
'Authorization': 'Bearer YOUR_API_TOKEN',
|
||||
'Version': '2021-07-28',
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json',
|
||||
'User-Agent': 'YourApp/1.0',
|
||||
}
|
||||
|
||||
conn.request('GET', '/v1/oauth/installedLocations?skip=1&limit=10&query=location name&isInstalled=True&companyId=tDtDnQdgm2LXpyiqYvZ6&appId=tDtDnQdgm2LXpyiqYvZ6&versionId=tDtDnQdgm2LXpyiqYvZ6&onTrial=True&planId=True&locationId=1245', headers=headers)
|
||||
|
||||
response = conn.getresponse()
|
||||
data = json.loads(response.read().decode())
|
||||
print(json.dumps(data, indent=2))
|
||||
conn.close()
|
||||
```
|
||||
|
||||
### n8n HTTP Node
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "GHL - GET installedLocations",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"parameters": {
|
||||
"method": "GET",
|
||||
"url": "https://rest.gohighlevel.com/v1/oauth/installedLocations",
|
||||
"authentication": "genericCredentialType",
|
||||
"genericAuthType": "httpHeaderAuth",
|
||||
"sendHeaders": true,
|
||||
"headerParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "Authorization",
|
||||
"value": "Bearer YOUR_API_TOKEN"
|
||||
},
|
||||
{
|
||||
"name": "Version",
|
||||
"value": "2021-07-28"
|
||||
},
|
||||
{
|
||||
"name": "Content-Type",
|
||||
"value": "application/json"
|
||||
},
|
||||
{
|
||||
"name": "Accept",
|
||||
"value": "application/json"
|
||||
},
|
||||
{
|
||||
"name": "User-Agent",
|
||||
"value": "YourApp/1.0"
|
||||
}
|
||||
]
|
||||
},
|
||||
"sendBody": false,
|
||||
"bodyParameters": {
|
||||
"parameters": []
|
||||
},
|
||||
"options": {},
|
||||
"sendQuery": true,
|
||||
"queryParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "skip",
|
||||
"value": "1"
|
||||
},
|
||||
{
|
||||
"name": "limit",
|
||||
"value": "10"
|
||||
},
|
||||
{
|
||||
"name": "query",
|
||||
"value": "location name"
|
||||
},
|
||||
{
|
||||
"name": "isInstalled",
|
||||
"value": true
|
||||
},
|
||||
{
|
||||
"name": "companyId",
|
||||
"value": "tDtDnQdgm2LXpyiqYvZ6"
|
||||
},
|
||||
{
|
||||
"name": "appId",
|
||||
"value": "tDtDnQdgm2LXpyiqYvZ6"
|
||||
},
|
||||
{
|
||||
"name": "versionId",
|
||||
"value": "tDtDnQdgm2LXpyiqYvZ6"
|
||||
},
|
||||
{
|
||||
"name": "onTrial",
|
||||
"value": true
|
||||
},
|
||||
{
|
||||
"name": "planId",
|
||||
"value": true
|
||||
},
|
||||
{
|
||||
"name": "locationId",
|
||||
"value": "1245"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**To use in n8n:**
|
||||
1. Add an HTTP Request node
|
||||
2. Switch to JSON mode (Parameters → use RAW JSON)
|
||||
3. Paste the JSON above
|
||||
4. Update `YOUR_API_TOKEN` with your actual GHL API key
|
||||
|
||||
### Request Parameters
|
||||
|
||||
| Name | Location | Type | Required | Description |
|
||||
|------|----------|------|----------|-------------|
|
||||
| `skip` | Query | `string` | — | Parameter to skip the number installed locations [example: `1`] |
|
||||
| `limit` | Query | `string` | — | Parameter to limit the number installed locations [example: `10`] |
|
||||
| `query` | Query | `string` | — | Parameter to search for the installed location by name [example: `location name`] |
|
||||
| `isInstalled` | Query | `boolean` | — | Filters out location which are installed for specified app under the specified company [example: ... |
|
||||
| `companyId` | Query | `string` | ✅ | Parameter to search by the companyId [example: `tDtDnQdgm2LXpyiqYvZ6`] |
|
||||
| `appId` | Query | `string` | ✅ | Parameter to search by the appId [example: `tDtDnQdgm2LXpyiqYvZ6`] |
|
||||
| `versionId` | Query | `string` | — | VersionId of the app [example: `tDtDnQdgm2LXpyiqYvZ6`] |
|
||||
| `onTrial` | Query | `boolean` | — | Filters out locations which are installed for specified app in trial mode [example: `True`] |
|
||||
| `planId` | Query | `string` | — | Filters out location which are installed for specified app under the specified planId [example: `... |
|
||||
| `locationId` | Query | `string` | — | locationId [example: `1245`] |
|
||||
|
||||
### Response Codes
|
||||
|
||||
| Code | Description | Schema |
|
||||
|------|-------------|--------|
|
||||
| `200` | Successful response | - **locations** (array of InstalledLocationSchema)<br> - **count** (number) (e.g. `1231`): Total location count under the company<br> - **installToFu... |
|
||||
| `400` | Bad Request | Standard error response for bad requests (400). Contains message, statusCode fields. |
|
||||
| `401` | Unauthorized | Standard error response for unauthorized requests (401). Contains message, statusCode fields. |
|
||||
| `422` | Unprocessable Entity | Standard error response for unprocessable entity (422). Contains message, statusCode, errors array fields. |
|
||||
|
||||
### ⚠️ Special Notes & Quirks
|
||||
|
||||
> ⚠️ **User-Agent Required:** All GHL API requests require a `User-Agent` HTTP header. Requests without it may be rejected with a 403 error.
|
||||
|
||||
---
|
||||
|
||||
## POST /oauth/locationToken
|
||||
|
||||
**Summary:** Get Location Access Token from Agency Token
|
||||
|
||||
This API allows you to generate locationAccessToken from AgencyAccessToken
|
||||
|
||||
**Version Header:** `2021-07-28`
|
||||
|
||||
**Operation ID:** `get-location-access-token`
|
||||
|
||||
**Tags:** OAuth 2.0
|
||||
|
||||
### cURL
|
||||
|
||||
```bash
|
||||
curl -X POST 'https://rest.gohighlevel.com/v1/oauth/locationToken' \
|
||||
-H 'Authorization: Bearer YOUR_API_TOKEN' \
|
||||
-H 'Version: 2021-07-28' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-H 'Accept: application/json' \
|
||||
-H 'User-Agent: YourApp/1.0'
|
||||
```
|
||||
|
||||
### Python (http.client)
|
||||
|
||||
```python
|
||||
import http.client
|
||||
import json
|
||||
|
||||
conn = http.client.HTTPSConnection('rest.gohighlevel.com')
|
||||
|
||||
headers = {
|
||||
'Authorization': 'Bearer YOUR_API_TOKEN',
|
||||
'Version': '2021-07-28',
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json',
|
||||
'User-Agent': 'YourApp/1.0',
|
||||
}
|
||||
|
||||
conn.request('POST', '/v1/oauth/locationToken', headers=headers)
|
||||
|
||||
response = conn.getresponse()
|
||||
data = json.loads(response.read().decode())
|
||||
print(json.dumps(data, indent=2))
|
||||
conn.close()
|
||||
```
|
||||
|
||||
### n8n HTTP Node
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "GHL - POST locationToken",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"parameters": {
|
||||
"method": "POST",
|
||||
"url": "https://rest.gohighlevel.com/v1/oauth/locationToken",
|
||||
"authentication": "genericCredentialType",
|
||||
"genericAuthType": "httpHeaderAuth",
|
||||
"sendHeaders": true,
|
||||
"headerParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "Authorization",
|
||||
"value": "Bearer YOUR_API_TOKEN"
|
||||
},
|
||||
{
|
||||
"name": "Version",
|
||||
"value": "2021-07-28"
|
||||
},
|
||||
{
|
||||
"name": "Content-Type",
|
||||
"value": "application/json"
|
||||
},
|
||||
{
|
||||
"name": "Accept",
|
||||
"value": "application/json"
|
||||
},
|
||||
{
|
||||
"name": "User-Agent",
|
||||
"value": "YourApp/1.0"
|
||||
}
|
||||
]
|
||||
},
|
||||
"sendBody": true,
|
||||
"bodyParameters": {
|
||||
"parameters": []
|
||||
},
|
||||
"options": {}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**To use in n8n:**
|
||||
1. Add an HTTP Request node
|
||||
2. Switch to JSON mode (Parameters → use RAW JSON)
|
||||
3. Paste the JSON above
|
||||
4. Update `YOUR_API_TOKEN` with your actual GHL API key
|
||||
|
||||
### Response Codes
|
||||
|
||||
| Code | Description | Schema |
|
||||
|------|-------------|--------|
|
||||
| `200` | Successful response | - **access_token** (string) (e.g. `ab12dc0ae1234a7898f9ff06d4f69gh`): Location access token which can be used to authenticate & authorize API und... |
|
||||
| `400` | Bad Request | Standard error response for bad requests (400). Contains message, statusCode fields. |
|
||||
| `401` | Unauthorized | Standard error response for unauthorized requests (401). Contains message, statusCode fields. |
|
||||
| `422` | Unprocessable Entity | Standard error response for unprocessable entity (422). Contains message, statusCode, errors array fields. |
|
||||
|
||||
### ⚠️ Special Notes & Quirks
|
||||
|
||||
> ⚠️ **User-Agent Required:** All GHL API requests require a `User-Agent` HTTP header. Requests without it may be rejected with a 403 error.
|
||||
|
||||
---
|
||||
|
||||
## POST /oauth/token
|
||||
|
||||
**Summary:** Get Access Token
|
||||
|
||||
Use Access Tokens to access GoHighLevel resources on behalf of an authenticated location/company.
|
||||
|
||||
**Version Header:** Not required
|
||||
|
||||
**Operation ID:** `get-access-token`
|
||||
|
||||
**Tags:** OAuth 2.0
|
||||
|
||||
### cURL
|
||||
|
||||
```bash
|
||||
curl -X POST 'https://rest.gohighlevel.com/v1/oauth/token' \
|
||||
-H 'Authorization: Bearer YOUR_API_TOKEN' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-H 'Accept: application/json' \
|
||||
-H 'User-Agent: YourApp/1.0'
|
||||
```
|
||||
|
||||
### Python (http.client)
|
||||
|
||||
```python
|
||||
import http.client
|
||||
import json
|
||||
|
||||
conn = http.client.HTTPSConnection('rest.gohighlevel.com')
|
||||
|
||||
headers = {
|
||||
'Authorization': 'Bearer YOUR_API_TOKEN',
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json',
|
||||
'User-Agent': 'YourApp/1.0',
|
||||
}
|
||||
|
||||
conn.request('POST', '/v1/oauth/token', headers=headers)
|
||||
|
||||
response = conn.getresponse()
|
||||
data = json.loads(response.read().decode())
|
||||
print(json.dumps(data, indent=2))
|
||||
conn.close()
|
||||
```
|
||||
|
||||
### n8n HTTP Node
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "GHL - POST token",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"parameters": {
|
||||
"method": "POST",
|
||||
"url": "https://rest.gohighlevel.com/v1/oauth/token",
|
||||
"authentication": "genericCredentialType",
|
||||
"genericAuthType": "httpHeaderAuth",
|
||||
"sendHeaders": true,
|
||||
"headerParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "Authorization",
|
||||
"value": "Bearer YOUR_API_TOKEN"
|
||||
},
|
||||
{
|
||||
"name": "Content-Type",
|
||||
"value": "application/json"
|
||||
},
|
||||
{
|
||||
"name": "Accept",
|
||||
"value": "application/json"
|
||||
},
|
||||
{
|
||||
"name": "User-Agent",
|
||||
"value": "YourApp/1.0"
|
||||
}
|
||||
]
|
||||
},
|
||||
"sendBody": true,
|
||||
"bodyParameters": {
|
||||
"parameters": []
|
||||
},
|
||||
"options": {}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**To use in n8n:**
|
||||
1. Add an HTTP Request node
|
||||
2. Switch to JSON mode (Parameters → use RAW JSON)
|
||||
3. Paste the JSON above
|
||||
4. Update `YOUR_API_TOKEN` with your actual GHL API key
|
||||
|
||||
### Response Codes
|
||||
|
||||
| Code | Description | Schema |
|
||||
|------|-------------|--------|
|
||||
| `200` | Successful response | - **access_token** (string) (e.g. `ab12dc0ae1234a7898f9ff06d4f69gh`)<br> - **token_type** (string) (e.g. `Bearer`)<br> - **expires_in** (number) (e.g... |
|
||||
| `400` | Bad Request | Standard error response for bad requests (400). Contains message, statusCode fields. |
|
||||
| `401` | Unauthorized | Standard error response for unauthorized requests (401). Contains message, statusCode fields. |
|
||||
| `422` | Unprocessable Entity | Standard error response for unprocessable entity (422). Contains message, statusCode, errors array fields. |
|
||||
|
||||
### ⚠️ Special Notes & Quirks
|
||||
|
||||
> ⚠️ **User-Agent Required:** All GHL API requests require a `User-Agent` HTTP header. Requests without it may be rejected with a 403 error.
|
||||
|
||||
---
|
||||
|
||||
|
||||
*Documentation generated from GHL OpenAPI specifications. Verify against official docs for latest changes.*
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,958 @@
|
||||
# GHL Users API
|
||||
|
||||
> ⚠️ **Deprecations:** Email update is deprecated. User deletion is asynchronous (returns 202 Accepted). `GET /users/` is deprecated — use `GET /users/search` instead.
|
||||
|
||||
**Endpoints:** 7
|
||||
**Base URL:** `https://rest.gohighlevel.com/v1`
|
||||
|
||||
**Version Header(s):** `2021-07-28`
|
||||
|
||||
---
|
||||
|
||||
## Endpoints
|
||||
|
||||
- [GET /users/](#get--users-) — Get User by Location
|
||||
- [POST /users/](#post--users-) — Create User
|
||||
- [GET /users/search](#get--users-search) — Search Users
|
||||
- [POST /users/search/filter-by-email](#post--users-search-filter-by-email) — Filter Users by Email
|
||||
- [DELETE /users/{userId}](#delete--users-userId) — Delete User
|
||||
- [GET /users/{userId}](#get--users-userId) — Get User
|
||||
- [PUT /users/{userId}](#put--users-userId) — Update User
|
||||
|
||||
---
|
||||
|
||||
## GET /users/
|
||||
|
||||
> ⚠️ **DEPRECATED** — This endpoint is deprecated and may be removed in a future version.
|
||||
|
||||
**Summary:** Get User by Location
|
||||
|
||||
Deprecated. Use `GET /users/search` instead. Pass `locationId` as a query parameter to filter results by location, along with the required `companyId` and other search filters as needed.
|
||||
|
||||
**Version Header:** `2021-07-28`
|
||||
|
||||
**Operation ID:** `get-user-by-location`
|
||||
|
||||
**Tags:** Users
|
||||
|
||||
### cURL
|
||||
|
||||
```bash
|
||||
curl -X GET 'https://rest.gohighlevel.com/v1/users/?locationId=s4BtzHFWmT28mbb85uPa' \
|
||||
-H 'Authorization: Bearer YOUR_API_TOKEN' \
|
||||
-H 'Version: 2021-07-28' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-H 'Accept: application/json' \
|
||||
-H 'User-Agent: YourApp/1.0'
|
||||
```
|
||||
|
||||
### Python (http.client)
|
||||
|
||||
```python
|
||||
import http.client
|
||||
import json
|
||||
|
||||
conn = http.client.HTTPSConnection('rest.gohighlevel.com')
|
||||
|
||||
headers = {
|
||||
'Authorization': 'Bearer YOUR_API_TOKEN',
|
||||
'Version': '2021-07-28',
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json',
|
||||
'User-Agent': 'YourApp/1.0',
|
||||
}
|
||||
|
||||
conn.request('GET', '/v1/users/?locationId=s4BtzHFWmT28mbb85uPa', headers=headers)
|
||||
|
||||
response = conn.getresponse()
|
||||
data = json.loads(response.read().decode())
|
||||
print(json.dumps(data, indent=2))
|
||||
conn.close()
|
||||
```
|
||||
|
||||
### n8n HTTP Node
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "GHL - GET users",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"parameters": {
|
||||
"method": "GET",
|
||||
"url": "https://rest.gohighlevel.com/v1/users/",
|
||||
"authentication": "genericCredentialType",
|
||||
"genericAuthType": "httpHeaderAuth",
|
||||
"sendHeaders": true,
|
||||
"headerParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "Authorization",
|
||||
"value": "Bearer YOUR_API_TOKEN"
|
||||
},
|
||||
{
|
||||
"name": "Version",
|
||||
"value": "2021-07-28"
|
||||
},
|
||||
{
|
||||
"name": "Content-Type",
|
||||
"value": "application/json"
|
||||
},
|
||||
{
|
||||
"name": "Accept",
|
||||
"value": "application/json"
|
||||
},
|
||||
{
|
||||
"name": "User-Agent",
|
||||
"value": "YourApp/1.0"
|
||||
}
|
||||
]
|
||||
},
|
||||
"sendBody": false,
|
||||
"bodyParameters": {
|
||||
"parameters": []
|
||||
},
|
||||
"options": {},
|
||||
"sendQuery": true,
|
||||
"queryParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "locationId",
|
||||
"value": "s4BtzHFWmT28mbb85uPa"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**To use in n8n:**
|
||||
1. Add an HTTP Request node
|
||||
2. Switch to JSON mode (Parameters → use RAW JSON)
|
||||
3. Paste the JSON above
|
||||
4. Update `YOUR_API_TOKEN` with your actual GHL API key
|
||||
|
||||
### Request Parameters
|
||||
|
||||
| Name | Location | Type | Required | Description |
|
||||
|------|----------|------|----------|-------------|
|
||||
| `locationId` | Query | `string` | ✅ | [example: `s4BtzHFWmT28mbb85uPa`] |
|
||||
|
||||
### Response Codes
|
||||
|
||||
| Code | Description | Schema |
|
||||
|------|-------------|--------|
|
||||
| `200` | Successful response | - **users** (array of UserSchema) |
|
||||
| `400` | Bad Request | Standard error response for bad requests (400). Contains message, statusCode fields. |
|
||||
| `401` | Unauthorized | Standard error response for unauthorized requests (401). Contains message, statusCode fields. |
|
||||
|
||||
### ⚠️ Special Notes & Quirks
|
||||
|
||||
> ⚠️ **User-Agent Required:** All GHL API requests require a `User-Agent` HTTP header. Requests without it may be rejected with a 403 error.
|
||||
|
||||
---
|
||||
|
||||
## POST /users/
|
||||
|
||||
**Summary:** Create User
|
||||
|
||||
Create User
|
||||
|
||||
**Version Header:** `2021-07-28`
|
||||
|
||||
**Operation ID:** `create-user`
|
||||
|
||||
**Tags:** Users
|
||||
|
||||
### cURL
|
||||
|
||||
```bash
|
||||
curl -X POST 'https://rest.gohighlevel.com/v1/users/' \
|
||||
-H 'Authorization: Bearer YOUR_API_TOKEN' \
|
||||
-H 'Version: 2021-07-28' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-H 'Accept: application/json' \
|
||||
-H 'User-Agent: YourApp/1.0' \
|
||||
-d '{}' # See request body schema below
|
||||
```
|
||||
|
||||
### Python (http.client)
|
||||
|
||||
```python
|
||||
import http.client
|
||||
import json
|
||||
|
||||
conn = http.client.HTTPSConnection('rest.gohighlevel.com')
|
||||
|
||||
headers = {
|
||||
'Authorization': 'Bearer YOUR_API_TOKEN',
|
||||
'Version': '2021-07-28',
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json',
|
||||
'User-Agent': 'YourApp/1.0',
|
||||
}
|
||||
|
||||
payload = json.dumps({})
|
||||
conn.request('POST', '/v1/users/', body=payload, headers=headers)
|
||||
|
||||
response = conn.getresponse()
|
||||
data = json.loads(response.read().decode())
|
||||
print(json.dumps(data, indent=2))
|
||||
conn.close()
|
||||
```
|
||||
|
||||
### n8n HTTP Node
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "GHL - POST users",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"parameters": {
|
||||
"method": "POST",
|
||||
"url": "https://rest.gohighlevel.com/v1/users/",
|
||||
"authentication": "genericCredentialType",
|
||||
"genericAuthType": "httpHeaderAuth",
|
||||
"sendHeaders": true,
|
||||
"headerParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "Authorization",
|
||||
"value": "Bearer YOUR_API_TOKEN"
|
||||
},
|
||||
{
|
||||
"name": "Version",
|
||||
"value": "2021-07-28"
|
||||
},
|
||||
{
|
||||
"name": "Content-Type",
|
||||
"value": "application/json"
|
||||
},
|
||||
{
|
||||
"name": "Accept",
|
||||
"value": "application/json"
|
||||
},
|
||||
{
|
||||
"name": "User-Agent",
|
||||
"value": "YourApp/1.0"
|
||||
}
|
||||
]
|
||||
},
|
||||
"sendBody": true,
|
||||
"bodyParameters": {
|
||||
"parameters": []
|
||||
},
|
||||
"options": {}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**To use in n8n:**
|
||||
1. Add an HTTP Request node
|
||||
2. Switch to JSON mode (Parameters → use RAW JSON)
|
||||
3. Paste the JSON above
|
||||
4. Update `YOUR_API_TOKEN` with your actual GHL API key
|
||||
|
||||
### Request Body Schema
|
||||
|
||||
- **companyId** (string) (required) (e.g. `ve9EPM428h8vShlRW1KT`)
|
||||
- **firstName** (string) (required) (e.g. `John`)
|
||||
- **lastName** (string) (required) (e.g. `Deo`)
|
||||
- **email** (string) (required) (e.g. `john@deo.com`)
|
||||
- **password** (string) (required) (e.g. `*******`)
|
||||
- **phone** (string) (e.g. `+18832327657`)
|
||||
- **type** (string) (required) (e.g. `account`)
|
||||
- **role** (string) (required) (e.g. `admin`)
|
||||
- **locationIds** (array) (required) (e.g. `['C2QujeCh8ZnC7al2InWR']`)
|
||||
- **permissions** (PermissionsDto)
|
||||
- **scopes** (array) (e.g. `['contacts.write', 'campaigns.readonly']`): Scopes allowed for users. Only scopes that have been passed will be enabled. Note:- If passed empty all the scopes will be get disabled
|
||||
- **scopesAssignedToOnly** (array) (e.g. `['contacts.write', 'campaigns.readonly']`): Assigned Scopes allowed for users. Only scopes that have been passed will be enabled. If passed empty all the assigned scopes will be get disabled
|
||||
- **profilePhoto** (string) (e.g. `https://img.png`)
|
||||
- **twilioPhone** (object) (e.g. `{'C2QujeCh8ZnC7al2InWR': '+18832327657', 'M2QrtfVt8ZnC7cv2InDL': '+18832327657'}`): Per-location inbound Twilio number in E.164 format, keyed by location id (Call and Voicemail Inbound Number for direct Twilio, not LC Phone). Replacement semantics: if you send twilioPhone in the request body, the stored map is replaced entirely with this object (not merged). Any location id omitted from the object is removed from the saved map. Omit the twilioPhone property entirely to leave existing numbers unchanged. Send an empty object {} to clear all per-location numbers. To clear a single location only, set that location id to an empty string "".
|
||||
- **platformLanguage** (string) (e.g. `en_US`): Platform language preference for the user
|
||||
|
||||
### Response Codes
|
||||
|
||||
| Code | Description | Schema |
|
||||
|------|-------------|--------|
|
||||
| `201` | Successful response | - **id** (string) (e.g. `0IHuJvc2ofPAAA8GzTRi`)<br> - **name** (string) (e.g. `John Deo`)<br> - **firstName** (string) (e.g. `John`)<br> - **lastName**... |
|
||||
| `400` | Bad Request | Standard error response for bad requests (400). Contains message, statusCode fields. |
|
||||
| `401` | Unauthorized | Standard error response for unauthorized requests (401). Contains message, statusCode fields. |
|
||||
| `422` | Unprocessable Entity | Standard error response for unprocessable entity (422). Contains message, statusCode, errors array fields. |
|
||||
|
||||
### ⚠️ Special Notes & Quirks
|
||||
|
||||
> ⚠️ **User-Agent Required:** All GHL API requests require a `User-Agent` HTTP header. Requests without it may be rejected with a 403 error.
|
||||
|
||||
---
|
||||
|
||||
## GET /users/search
|
||||
|
||||
**Summary:** Search Users
|
||||
|
||||
Search Users
|
||||
|
||||
**Version Header:** `2021-07-28`
|
||||
|
||||
**Operation ID:** `search-users`
|
||||
|
||||
**Tags:** Search
|
||||
|
||||
### cURL
|
||||
|
||||
```bash
|
||||
curl -X GET 'https://rest.gohighlevel.com/v1/users/search?companyId=5DP41231LkQsiKESj6rh&query=John&skip=1&limit=10&locationId=5DP41231LkQsiKESj6rh&type=agency&role=admin&ids=5DP4iH6HLkQsiKESj6rh,5DP4iH6HLkQsiKESj34h&sort=dateAdded&sortDirection=asc&enabled2waySync=VALUE' \
|
||||
-H 'Authorization: Bearer YOUR_API_TOKEN' \
|
||||
-H 'Version: 2021-07-28' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-H 'Accept: application/json' \
|
||||
-H 'User-Agent: YourApp/1.0'
|
||||
```
|
||||
|
||||
### Python (http.client)
|
||||
|
||||
```python
|
||||
import http.client
|
||||
import json
|
||||
|
||||
conn = http.client.HTTPSConnection('rest.gohighlevel.com')
|
||||
|
||||
headers = {
|
||||
'Authorization': 'Bearer YOUR_API_TOKEN',
|
||||
'Version': '2021-07-28',
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json',
|
||||
'User-Agent': 'YourApp/1.0',
|
||||
}
|
||||
|
||||
conn.request('GET', '/v1/users/search?companyId=5DP41231LkQsiKESj6rh&query=John&skip=1&limit=10&locationId=5DP41231LkQsiKESj6rh&type=agency&role=admin&ids=5DP4iH6HLkQsiKESj6rh,5DP4iH6HLkQsiKESj34h&sort=dateAdded&sortDirection=asc&enabled2waySync=VALUE', headers=headers)
|
||||
|
||||
response = conn.getresponse()
|
||||
data = json.loads(response.read().decode())
|
||||
print(json.dumps(data, indent=2))
|
||||
conn.close()
|
||||
```
|
||||
|
||||
### n8n HTTP Node
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "GHL - GET search",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"parameters": {
|
||||
"method": "GET",
|
||||
"url": "https://rest.gohighlevel.com/v1/users/search",
|
||||
"authentication": "genericCredentialType",
|
||||
"genericAuthType": "httpHeaderAuth",
|
||||
"sendHeaders": true,
|
||||
"headerParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "Authorization",
|
||||
"value": "Bearer YOUR_API_TOKEN"
|
||||
},
|
||||
{
|
||||
"name": "Version",
|
||||
"value": "2021-07-28"
|
||||
},
|
||||
{
|
||||
"name": "Content-Type",
|
||||
"value": "application/json"
|
||||
},
|
||||
{
|
||||
"name": "Accept",
|
||||
"value": "application/json"
|
||||
},
|
||||
{
|
||||
"name": "User-Agent",
|
||||
"value": "YourApp/1.0"
|
||||
}
|
||||
]
|
||||
},
|
||||
"sendBody": false,
|
||||
"bodyParameters": {
|
||||
"parameters": []
|
||||
},
|
||||
"options": {},
|
||||
"sendQuery": true,
|
||||
"queryParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "companyId",
|
||||
"value": "5DP41231LkQsiKESj6rh"
|
||||
},
|
||||
{
|
||||
"name": "query",
|
||||
"value": "John"
|
||||
},
|
||||
{
|
||||
"name": "skip",
|
||||
"value": "1"
|
||||
},
|
||||
{
|
||||
"name": "limit",
|
||||
"value": "10"
|
||||
},
|
||||
{
|
||||
"name": "locationId",
|
||||
"value": "5DP41231LkQsiKESj6rh"
|
||||
},
|
||||
{
|
||||
"name": "type",
|
||||
"value": "agency"
|
||||
},
|
||||
{
|
||||
"name": "role",
|
||||
"value": "admin"
|
||||
},
|
||||
{
|
||||
"name": "ids",
|
||||
"value": "5DP4iH6HLkQsiKESj6rh,5DP4iH6HLkQsiKESj34h"
|
||||
},
|
||||
{
|
||||
"name": "sort",
|
||||
"value": "dateAdded"
|
||||
},
|
||||
{
|
||||
"name": "sortDirection",
|
||||
"value": "asc"
|
||||
},
|
||||
{
|
||||
"name": "enabled2waySync",
|
||||
"value": "VALUE"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**To use in n8n:**
|
||||
1. Add an HTTP Request node
|
||||
2. Switch to JSON mode (Parameters → use RAW JSON)
|
||||
3. Paste the JSON above
|
||||
4. Update `YOUR_API_TOKEN` with your actual GHL API key
|
||||
|
||||
### Request Parameters
|
||||
|
||||
| Name | Location | Type | Required | Description |
|
||||
|------|----------|------|----------|-------------|
|
||||
| `companyId` | Query | `string` | ✅ | Company ID in which the search needs to be performed [example: `5DP41231LkQsiKESj6rh`] |
|
||||
| `query` | Query | `string` | — | The search term for the user is matched based on the user full name, email or phone [example: `Jo... |
|
||||
| `skip` | Query | `string` | — | No of results to be skipped before returning the result [example: `1`] |
|
||||
| `limit` | Query | `string` | — | No of results to be limited before returning the result [example: `10`] |
|
||||
| `locationId` | Query | `string` | — | Location ID in which the search needs to be performed [example: `5DP41231LkQsiKESj6rh`] |
|
||||
| `type` | Query | `string` | — | Type of the users to be filtered in the search [example: `agency`] |
|
||||
| `role` | Query | `string` | — | Role of the users to be filtered in the search [example: `admin`] |
|
||||
| `ids` | Query | `string` | — | List of User IDs to be filtered in the search [example: `5DP4iH6HLkQsiKESj6rh,5DP4iH6HLkQsiKESj34h`] |
|
||||
| `sort` | Query | `string` | — | The field on which sort is applied in which the results need to be sorted. Default is based on th... |
|
||||
| `sortDirection` | Query | `string` | — | The direction in which the results need to be sorted [example: `asc`] |
|
||||
| `enabled2waySync` | Query | `boolean` | — | |
|
||||
|
||||
### Response Codes
|
||||
|
||||
| Code | Description | Schema |
|
||||
|------|-------------|--------|
|
||||
| `200` | Successful response | - **users** (array of UserSchema)<br> - **count** (number) (e.g. `1231`) |
|
||||
| `400` | Bad Request | Standard error response for bad requests (400). Contains message, statusCode fields. |
|
||||
| `401` | Unauthorized | Standard error response for unauthorized requests (401). Contains message, statusCode fields. |
|
||||
| `422` | Unprocessable Entity | Standard error response for unprocessable entity (422). Contains message, statusCode, errors array fields. |
|
||||
|
||||
### ⚠️ Special Notes & Quirks
|
||||
|
||||
> ⚠️ **User-Agent Required:** All GHL API requests require a `User-Agent` HTTP header. Requests without it may be rejected with a 403 error.
|
||||
|
||||
---
|
||||
|
||||
## POST /users/search/filter-by-email
|
||||
|
||||
**Summary:** Filter Users by Email
|
||||
|
||||
Filter users by company ID, deleted status, and email array
|
||||
|
||||
**Version Header:** `2021-07-28`
|
||||
|
||||
**Operation ID:** `filter-users-by-email`
|
||||
|
||||
**Tags:** Search
|
||||
|
||||
### cURL
|
||||
|
||||
```bash
|
||||
curl -X POST 'https://rest.gohighlevel.com/v1/users/search/filter-by-email' \
|
||||
-H 'Authorization: Bearer YOUR_API_TOKEN' \
|
||||
-H 'Version: 2021-07-28' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-H 'Accept: application/json' \
|
||||
-H 'User-Agent: YourApp/1.0' \
|
||||
-d '{}' # See request body schema below
|
||||
```
|
||||
|
||||
### Python (http.client)
|
||||
|
||||
```python
|
||||
import http.client
|
||||
import json
|
||||
|
||||
conn = http.client.HTTPSConnection('rest.gohighlevel.com')
|
||||
|
||||
headers = {
|
||||
'Authorization': 'Bearer YOUR_API_TOKEN',
|
||||
'Version': '2021-07-28',
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json',
|
||||
'User-Agent': 'YourApp/1.0',
|
||||
}
|
||||
|
||||
payload = json.dumps({})
|
||||
conn.request('POST', '/v1/users/search/filter-by-email', body=payload, headers=headers)
|
||||
|
||||
response = conn.getresponse()
|
||||
data = json.loads(response.read().decode())
|
||||
print(json.dumps(data, indent=2))
|
||||
conn.close()
|
||||
```
|
||||
|
||||
### n8n HTTP Node
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "GHL - POST filter-by-email",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"parameters": {
|
||||
"method": "POST",
|
||||
"url": "https://rest.gohighlevel.com/v1/users/search/filter-by-email",
|
||||
"authentication": "genericCredentialType",
|
||||
"genericAuthType": "httpHeaderAuth",
|
||||
"sendHeaders": true,
|
||||
"headerParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "Authorization",
|
||||
"value": "Bearer YOUR_API_TOKEN"
|
||||
},
|
||||
{
|
||||
"name": "Version",
|
||||
"value": "2021-07-28"
|
||||
},
|
||||
{
|
||||
"name": "Content-Type",
|
||||
"value": "application/json"
|
||||
},
|
||||
{
|
||||
"name": "Accept",
|
||||
"value": "application/json"
|
||||
},
|
||||
{
|
||||
"name": "User-Agent",
|
||||
"value": "YourApp/1.0"
|
||||
}
|
||||
]
|
||||
},
|
||||
"sendBody": true,
|
||||
"bodyParameters": {
|
||||
"parameters": []
|
||||
},
|
||||
"options": {}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**To use in n8n:**
|
||||
1. Add an HTTP Request node
|
||||
2. Switch to JSON mode (Parameters → use RAW JSON)
|
||||
3. Paste the JSON above
|
||||
4. Update `YOUR_API_TOKEN` with your actual GHL API key
|
||||
|
||||
### Request Body Schema
|
||||
|
||||
- **companyId** (string) (required) (e.g. `5DP41231LkQsiKESj6rh`): Company ID to filter users
|
||||
- **emails** (string) (required) (e.g. `user1@example.com,user2@example.com`): Comma-separated list of email addresses to filter users
|
||||
- **deleted** (boolean) (e.g. `False`): Filter deleted users
|
||||
- **skip** (string) (e.g. `1`): No of results to be skipped before returning the result
|
||||
- **limit** (string) (e.g. `10`): No of results to be limited before returning the result
|
||||
- **projection** (string) (e.g. `all`): Projection fields to return. Use "all" for all fields, or specify comma-separated field names. Default returns only id and email
|
||||
|
||||
### Response Codes
|
||||
|
||||
| Code | Description | Schema |
|
||||
|------|-------------|--------|
|
||||
| `200` | Successful response | - **users** (array of UserSchema)<br> - **count** (number) (e.g. `1231`) |
|
||||
| `400` | Bad Request | Standard error response for bad requests (400). Contains message, statusCode fields. |
|
||||
| `401` | Unauthorized | Standard error response for unauthorized requests (401). Contains message, statusCode fields. |
|
||||
| `422` | Unprocessable Entity | Standard error response for unprocessable entity (422). Contains message, statusCode, errors array fields. |
|
||||
|
||||
### ⚠️ Special Notes & Quirks
|
||||
|
||||
> ⚠️ **User-Agent Required:** All GHL API requests require a `User-Agent` HTTP header. Requests without it may be rejected with a 403 error.
|
||||
|
||||
> 📌 **POST for Search:** Search endpoints use POST instead of GET because the search filters are complex JSON objects that can't be easily encoded in query parameters.
|
||||
|
||||
---
|
||||
|
||||
## DELETE /users/{userId}
|
||||
|
||||
**Summary:** Delete User
|
||||
|
||||
Delete User
|
||||
|
||||
**Version Header:** `2021-07-28`
|
||||
|
||||
**Operation ID:** `delete-user`
|
||||
|
||||
**Tags:** Users
|
||||
|
||||
### cURL
|
||||
|
||||
```bash
|
||||
curl -X DELETE 'https://rest.gohighlevel.com/v1/users/{userId}' \
|
||||
-H 'Authorization: Bearer YOUR_API_TOKEN' \
|
||||
-H 'Version: 2021-07-28' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-H 'Accept: application/json' \
|
||||
-H 'User-Agent: YourApp/1.0'
|
||||
```
|
||||
|
||||
### Python (http.client)
|
||||
|
||||
```python
|
||||
import http.client
|
||||
import json
|
||||
|
||||
conn = http.client.HTTPSConnection('rest.gohighlevel.com')
|
||||
|
||||
headers = {
|
||||
'Authorization': 'Bearer YOUR_API_TOKEN',
|
||||
'Version': '2021-07-28',
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json',
|
||||
'User-Agent': 'YourApp/1.0',
|
||||
}
|
||||
|
||||
conn.request('DELETE', '/v1/users/{userId}', headers=headers)
|
||||
|
||||
response = conn.getresponse()
|
||||
data = json.loads(response.read().decode())
|
||||
print(json.dumps(data, indent=2))
|
||||
conn.close()
|
||||
```
|
||||
|
||||
### n8n HTTP Node
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "GHL - DELETE {userId}",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"parameters": {
|
||||
"method": "DELETE",
|
||||
"url": "https://rest.gohighlevel.com/v1/users/{userId}",
|
||||
"authentication": "genericCredentialType",
|
||||
"genericAuthType": "httpHeaderAuth",
|
||||
"sendHeaders": true,
|
||||
"headerParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "Authorization",
|
||||
"value": "Bearer YOUR_API_TOKEN"
|
||||
},
|
||||
{
|
||||
"name": "Version",
|
||||
"value": "2021-07-28"
|
||||
},
|
||||
{
|
||||
"name": "Content-Type",
|
||||
"value": "application/json"
|
||||
},
|
||||
{
|
||||
"name": "Accept",
|
||||
"value": "application/json"
|
||||
},
|
||||
{
|
||||
"name": "User-Agent",
|
||||
"value": "YourApp/1.0"
|
||||
}
|
||||
]
|
||||
},
|
||||
"sendBody": false,
|
||||
"bodyParameters": {
|
||||
"parameters": []
|
||||
},
|
||||
"options": {}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**To use in n8n:**
|
||||
1. Add an HTTP Request node
|
||||
2. Switch to JSON mode (Parameters → use RAW JSON)
|
||||
3. Paste the JSON above
|
||||
4. Update `YOUR_API_TOKEN` with your actual GHL API key
|
||||
|
||||
### Response Codes
|
||||
|
||||
| Code | Description | Schema |
|
||||
|------|-------------|--------|
|
||||
| `200` | Successful response | - **succeded** (boolean) (e.g. `True`)<br> - **message** (string) (e.g. `Queued deleting user with e-mail john@deo.com and name John Deo. Will take... |
|
||||
| `400` | Bad Request | Standard error response for bad requests (400). Contains message, statusCode fields. |
|
||||
| `401` | Unauthorized | Standard error response for unauthorized requests (401). Contains message, statusCode fields. |
|
||||
| `422` | Unprocessable Entity | Standard error response for unprocessable entity (422). Contains message, statusCode, errors array fields. |
|
||||
|
||||
### ⚠️ Special Notes & Quirks
|
||||
|
||||
> ⚠️ **User-Agent Required:** All GHL API requests require a `User-Agent` HTTP header. Requests without it may be rejected with a 403 error.
|
||||
|
||||
> ⚠️ **Async Delete:** User deletion is asynchronous. The API returns `202 Accepted` but the user may not be deleted immediately.
|
||||
|
||||
---
|
||||
|
||||
## GET /users/{userId}
|
||||
|
||||
**Summary:** Get User
|
||||
|
||||
Get User
|
||||
|
||||
**Version Header:** `2021-07-28`
|
||||
|
||||
**Operation ID:** `get-user`
|
||||
|
||||
**Tags:** Users
|
||||
|
||||
### cURL
|
||||
|
||||
```bash
|
||||
curl -X GET 'https://rest.gohighlevel.com/v1/users/ve9EPM428h8vShlRW1KT' \
|
||||
-H 'Authorization: Bearer YOUR_API_TOKEN' \
|
||||
-H 'Version: 2021-07-28' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-H 'Accept: application/json' \
|
||||
-H 'User-Agent: YourApp/1.0'
|
||||
```
|
||||
|
||||
### Python (http.client)
|
||||
|
||||
```python
|
||||
import http.client
|
||||
import json
|
||||
|
||||
conn = http.client.HTTPSConnection('rest.gohighlevel.com')
|
||||
|
||||
headers = {
|
||||
'Authorization': 'Bearer YOUR_API_TOKEN',
|
||||
'Version': '2021-07-28',
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json',
|
||||
'User-Agent': 'YourApp/1.0',
|
||||
}
|
||||
|
||||
conn.request('GET', '/v1/users/ve9EPM428h8vShlRW1KT', headers=headers)
|
||||
|
||||
response = conn.getresponse()
|
||||
data = json.loads(response.read().decode())
|
||||
print(json.dumps(data, indent=2))
|
||||
conn.close()
|
||||
```
|
||||
|
||||
### n8n HTTP Node
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "GHL - GET ve9EPM428h8vShlRW1KT",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"parameters": {
|
||||
"method": "GET",
|
||||
"url": "https://rest.gohighlevel.com/v1/users/ve9EPM428h8vShlRW1KT",
|
||||
"authentication": "genericCredentialType",
|
||||
"genericAuthType": "httpHeaderAuth",
|
||||
"sendHeaders": true,
|
||||
"headerParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "Authorization",
|
||||
"value": "Bearer YOUR_API_TOKEN"
|
||||
},
|
||||
{
|
||||
"name": "Version",
|
||||
"value": "2021-07-28"
|
||||
},
|
||||
{
|
||||
"name": "Content-Type",
|
||||
"value": "application/json"
|
||||
},
|
||||
{
|
||||
"name": "Accept",
|
||||
"value": "application/json"
|
||||
},
|
||||
{
|
||||
"name": "User-Agent",
|
||||
"value": "YourApp/1.0"
|
||||
}
|
||||
]
|
||||
},
|
||||
"sendBody": false,
|
||||
"bodyParameters": {
|
||||
"parameters": []
|
||||
},
|
||||
"options": {}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**To use in n8n:**
|
||||
1. Add an HTTP Request node
|
||||
2. Switch to JSON mode (Parameters → use RAW JSON)
|
||||
3. Paste the JSON above
|
||||
4. Update `YOUR_API_TOKEN` with your actual GHL API key
|
||||
|
||||
### Request Parameters
|
||||
|
||||
| Name | Location | Type | Required | Description |
|
||||
|------|----------|------|----------|-------------|
|
||||
| `userId` | Path | `string` | ✅ | User Id [example: `ve9EPM428h8vShlRW1KT`] |
|
||||
|
||||
### Response Codes
|
||||
|
||||
| Code | Description | Schema |
|
||||
|------|-------------|--------|
|
||||
| `200` | Successful response | - **id** (string) (e.g. `0IHuJvc2ofPAAA8GzTRi`)<br> - **name** (string) (e.g. `John Deo`)<br> - **firstName** (string) (e.g. `John`)<br> - **lastName**... |
|
||||
| `400` | Bad Request | Standard error response for bad requests (400). Contains message, statusCode fields. |
|
||||
| `401` | Unauthorized | Standard error response for unauthorized requests (401). Contains message, statusCode fields. |
|
||||
| `422` | Unprocessable Entity | Standard error response for unprocessable entity (422). Contains message, statusCode, errors array fields. |
|
||||
|
||||
### ⚠️ Special Notes & Quirks
|
||||
|
||||
> ⚠️ **User-Agent Required:** All GHL API requests require a `User-Agent` HTTP header. Requests without it may be rejected with a 403 error.
|
||||
|
||||
---
|
||||
|
||||
## PUT /users/{userId}
|
||||
|
||||
**Summary:** Update User
|
||||
|
||||
Update User
|
||||
|
||||
**Version Header:** `2021-07-28`
|
||||
|
||||
**Operation ID:** `update-user`
|
||||
|
||||
**Tags:** Users
|
||||
|
||||
### cURL
|
||||
|
||||
```bash
|
||||
curl -X PUT 'https://rest.gohighlevel.com/v1/users/{userId}' \
|
||||
-H 'Authorization: Bearer YOUR_API_TOKEN' \
|
||||
-H 'Version: 2021-07-28' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-H 'Accept: application/json' \
|
||||
-H 'User-Agent: YourApp/1.0' \
|
||||
-d '{}' # See request body schema below
|
||||
```
|
||||
|
||||
### Python (http.client)
|
||||
|
||||
```python
|
||||
import http.client
|
||||
import json
|
||||
|
||||
conn = http.client.HTTPSConnection('rest.gohighlevel.com')
|
||||
|
||||
headers = {
|
||||
'Authorization': 'Bearer YOUR_API_TOKEN',
|
||||
'Version': '2021-07-28',
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json',
|
||||
'User-Agent': 'YourApp/1.0',
|
||||
}
|
||||
|
||||
payload = json.dumps({})
|
||||
conn.request('PUT', '/v1/users/{userId}', body=payload, headers=headers)
|
||||
|
||||
response = conn.getresponse()
|
||||
data = json.loads(response.read().decode())
|
||||
print(json.dumps(data, indent=2))
|
||||
conn.close()
|
||||
```
|
||||
|
||||
### n8n HTTP Node
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "GHL - PUT {userId}",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"parameters": {
|
||||
"method": "PUT",
|
||||
"url": "https://rest.gohighlevel.com/v1/users/{userId}",
|
||||
"authentication": "genericCredentialType",
|
||||
"genericAuthType": "httpHeaderAuth",
|
||||
"sendHeaders": true,
|
||||
"headerParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "Authorization",
|
||||
"value": "Bearer YOUR_API_TOKEN"
|
||||
},
|
||||
{
|
||||
"name": "Version",
|
||||
"value": "2021-07-28"
|
||||
},
|
||||
{
|
||||
"name": "Content-Type",
|
||||
"value": "application/json"
|
||||
},
|
||||
{
|
||||
"name": "Accept",
|
||||
"value": "application/json"
|
||||
},
|
||||
{
|
||||
"name": "User-Agent",
|
||||
"value": "YourApp/1.0"
|
||||
}
|
||||
]
|
||||
},
|
||||
"sendBody": true,
|
||||
"bodyParameters": {
|
||||
"parameters": []
|
||||
},
|
||||
"options": {}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**To use in n8n:**
|
||||
1. Add an HTTP Request node
|
||||
2. Switch to JSON mode (Parameters → use RAW JSON)
|
||||
3. Paste the JSON above
|
||||
4. Update `YOUR_API_TOKEN` with your actual GHL API key
|
||||
|
||||
### Request Body Schema
|
||||
|
||||
- **firstName** (string) (e.g. `John`)
|
||||
- **lastName** (string) (e.g. `Deo`)
|
||||
- **email** (string) (e.g. `john@deo.com`): Email update is no longer supported due to security reasons.
|
||||
- **password** (string) (e.g. `*******`)
|
||||
- **phone** (string) (e.g. `+18832327657`)
|
||||
- **type** (string) (e.g. `account`)
|
||||
- **role** (string) (e.g. `admin`)
|
||||
- **companyId** (string) (e.g. `UAXssdawIWAWD`): Company/Agency Id. Required for Agency Level access
|
||||
- **locationIds** (array) (e.g. `['C2QujeCh8ZnC7al2InWR']`)
|
||||
- **permissions** (PermissionsDto)
|
||||
- **scopes** (array) (e.g. `['contacts.write', 'campaigns.readonly']`): Scopes allowed for users. Only scopes that have been passed will be enabled. If passed empty all the scopes will be get disabled
|
||||
- **scopesAssignedToOnly** (array) (e.g. `['contacts.write', 'campaigns.readonly']`): Assigned Scopes allowed for users. Only scopes that have been passed will be enabled. If passed empty all the assigned scopes will be get disabled
|
||||
- **profilePhoto** (string) (e.g. `https://img.png`)
|
||||
- **twilioPhone** (object) (e.g. `{'C2QujeCh8ZnC7al2InWR': '+18832327657', 'M2QrtfVt8ZnC7cv2InDL': '+18832327657'}`): Per-location inbound Twilio number in E.164 format, keyed by location id (Call and Voicemail Inbound Number for direct Twilio, not LC Phone). Replacement semantics: if you send twilioPhone in the request body, the stored map is replaced entirely with this object (not merged). Any location id omitted from the object is removed from the saved map. Omit the twilioPhone property entirely to leave existing numbers unchanged. Send an empty object {} to clear all per-location numbers. To clear a single location only, set that location id to an empty string "".
|
||||
- **platformLanguage** (string) (e.g. `en_US`): Platform language preference for the user
|
||||
|
||||
### Response Codes
|
||||
|
||||
| Code | Description | Schema |
|
||||
|------|-------------|--------|
|
||||
| `200` | Successful response | - **id** (string) (e.g. `0IHuJvc2ofPAAA8GzTRi`)<br> - **name** (string) (e.g. `John Deo`)<br> - **firstName** (string) (e.g. `John`)<br> - **lastName**... |
|
||||
| `400` | Bad Request | Standard error response for bad requests (400). Contains message, statusCode fields. |
|
||||
| `401` | Unauthorized | Standard error response for unauthorized requests (401). Contains message, statusCode fields. |
|
||||
| `422` | Unprocessable Entity | Standard error response for unprocessable entity (422). Contains message, statusCode, errors array fields. |
|
||||
|
||||
### ⚠️ Special Notes & Quirks
|
||||
|
||||
> ⚠️ **User-Agent Required:** All GHL API requests require a `User-Agent` HTTP header. Requests without it may be rejected with a 403 error.
|
||||
|
||||
---
|
||||
|
||||
|
||||
*Documentation generated from GHL OpenAPI specifications. Verify against official docs for latest changes.*
|
||||
@@ -0,0 +1,145 @@
|
||||
# GHL Workflows API
|
||||
|
||||
**Endpoints:** 1
|
||||
**Base URL:** `https://rest.gohighlevel.com/v1`
|
||||
|
||||
**Version Header(s):** `2021-07-28`
|
||||
|
||||
---
|
||||
|
||||
## Endpoints
|
||||
|
||||
- [GET /workflows/](#get--workflows-) — Get Workflow
|
||||
|
||||
---
|
||||
|
||||
## GET /workflows/
|
||||
|
||||
**Summary:** Get Workflow
|
||||
|
||||
Get Workflow
|
||||
|
||||
**Version Header:** `2021-07-28`
|
||||
|
||||
**Operation ID:** `get-workflow`
|
||||
|
||||
**Tags:** Workflows
|
||||
|
||||
### cURL
|
||||
|
||||
```bash
|
||||
curl -X GET 'https://rest.gohighlevel.com/v1/workflows/?locationId=VALUE' \
|
||||
-H 'Authorization: Bearer YOUR_API_TOKEN' \
|
||||
-H 'Version: 2021-07-28' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-H 'Accept: application/json' \
|
||||
-H 'User-Agent: YourApp/1.0'
|
||||
```
|
||||
|
||||
### Python (http.client)
|
||||
|
||||
```python
|
||||
import http.client
|
||||
import json
|
||||
|
||||
conn = http.client.HTTPSConnection('rest.gohighlevel.com')
|
||||
|
||||
headers = {
|
||||
'Authorization': 'Bearer YOUR_API_TOKEN',
|
||||
'Version': '2021-07-28',
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json',
|
||||
'User-Agent': 'YourApp/1.0',
|
||||
}
|
||||
|
||||
conn.request('GET', '/v1/workflows/?locationId=VALUE', headers=headers)
|
||||
|
||||
response = conn.getresponse()
|
||||
data = json.loads(response.read().decode())
|
||||
print(json.dumps(data, indent=2))
|
||||
conn.close()
|
||||
```
|
||||
|
||||
### n8n HTTP Node
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "GHL - GET workflows",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"parameters": {
|
||||
"method": "GET",
|
||||
"url": "https://rest.gohighlevel.com/v1/workflows/",
|
||||
"authentication": "genericCredentialType",
|
||||
"genericAuthType": "httpHeaderAuth",
|
||||
"sendHeaders": true,
|
||||
"headerParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "Authorization",
|
||||
"value": "Bearer YOUR_API_TOKEN"
|
||||
},
|
||||
{
|
||||
"name": "Version",
|
||||
"value": "2021-07-28"
|
||||
},
|
||||
{
|
||||
"name": "Content-Type",
|
||||
"value": "application/json"
|
||||
},
|
||||
{
|
||||
"name": "Accept",
|
||||
"value": "application/json"
|
||||
},
|
||||
{
|
||||
"name": "User-Agent",
|
||||
"value": "YourApp/1.0"
|
||||
}
|
||||
]
|
||||
},
|
||||
"sendBody": false,
|
||||
"bodyParameters": {
|
||||
"parameters": []
|
||||
},
|
||||
"options": {},
|
||||
"sendQuery": true,
|
||||
"queryParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "locationId",
|
||||
"value": "VALUE"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**To use in n8n:**
|
||||
1. Add an HTTP Request node
|
||||
2. Switch to JSON mode (Parameters → use RAW JSON)
|
||||
3. Paste the JSON above
|
||||
4. Update `YOUR_API_TOKEN` with your actual GHL API key
|
||||
|
||||
### Request Parameters
|
||||
|
||||
| Name | Location | Type | Required | Description |
|
||||
|------|----------|------|----------|-------------|
|
||||
| `locationId` | Query | `string` | ✅ | |
|
||||
|
||||
### Response Codes
|
||||
|
||||
| Code | Description | Schema |
|
||||
|------|-------------|--------|
|
||||
| `200` | Successful response | - **workflows** (array of WorkflowSchema) |
|
||||
| `400` | Bad Request | Standard error response for bad requests (400). Contains message, statusCode fields. |
|
||||
| `401` | Unauthorized | Standard error response for unauthorized requests (401). Contains message, statusCode fields. |
|
||||
| `422` | Unprocessable Entity | Standard error response for unprocessable entity (422). Contains message, statusCode, errors array fields. |
|
||||
|
||||
### ⚠️ Special Notes & Quirks
|
||||
|
||||
> ⚠️ **User-Agent Required:** All GHL API requests require a `User-Agent` HTTP header. Requests without it may be rejected with a 403 error.
|
||||
|
||||
---
|
||||
|
||||
|
||||
*Documentation generated from GHL OpenAPI specifications. Verify against official docs for latest changes.*
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1 @@
|
||||
Dentro de n8n para poder utilizar la Verificador de sucursales y poder obtener un listado de Los datos de acceso utilizamos una instancia de baserow donde esta cargada la información que se usa.
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
+1749
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,93 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""Agrega al workflow n8n de oportunidades un nodo HTTP PUT que, antes de
|
||||
'Buscar Oportunidad - SUCURSAL', setea el campo 'ID Oportunidad Sucursal' de la
|
||||
opp de sucursal = su propio id nativo (del webhook). Reordena la rama sucursal
|
||||
para que 'Conseguir Custom Fields - Opportunity - SUCURSAL' vaya antes (necesario
|
||||
para resolver el field_id, que varia por sucursal). Hace backup y verifica.
|
||||
"""
|
||||
import requests, json, datetime, uuid
|
||||
|
||||
api_key = base = None
|
||||
with open('n8n/n8n credencials.txt', encoding='utf-8') as f:
|
||||
for line in f:
|
||||
if line.startswith('API:'): api_key = line.split('API:', 1)[1].strip()
|
||||
if line.startswith('URL:'): base = line.split('URL:', 1)[1].strip().rstrip('/')
|
||||
H = {'X-N8N-API-KEY': api_key, 'Accept': 'application/json', 'Content-Type': 'application/json'}
|
||||
WID = 'Cfgwp0bOtDW8zuKW'
|
||||
|
||||
wf = requests.get(f'{base}/api/v1/workflows/{WID}', headers=H, timeout=30).json()
|
||||
print('versionId previo:', wf.get('versionId'))
|
||||
|
||||
ts = datetime.datetime.now().strftime('%Y%m%d_%H%M%S')
|
||||
bpath = f'n8n/backup_workflow_pre_automap_{ts}.json'
|
||||
open(bpath, 'w', encoding='utf-8').write(json.dumps(wf, ensure_ascii=False, indent=2))
|
||||
print('backup:', bpath)
|
||||
|
||||
nodes = wf['nodes']; conns = wf['connections']
|
||||
NEW_NAME = 'Mapear ID Oportunidad Sucursal - SUCURSAL'
|
||||
if any(n.get('name') == NEW_NAME for n in nodes):
|
||||
print('El nodo ya existe; abortando para no duplicar.'); raise SystemExit
|
||||
|
||||
jsonBody = ("={{ JSON.stringify({ customFields: [ { "
|
||||
"id: $('Conseguir Custom Fields - Opportunity - SUCURSAL').item.json.customFields"
|
||||
".find(f => f.fieldKey === 'opportunity.id_oportunidad_sucursal')?.id, "
|
||||
"key: 'opportunity.id_oportunidad_sucursal', "
|
||||
"field_value: $('Datos de Lead').item.json.Oportunidad.opportunity_id } ] }) }}")
|
||||
|
||||
new_node = {
|
||||
"parameters": {
|
||||
"method": "PUT",
|
||||
"url": "=https://services.leadconnectorhq.com/opportunities/{{ $('Datos de Lead').item.json.Oportunidad.opportunity_id }}",
|
||||
"sendHeaders": True,
|
||||
"headerParameters": {"parameters": [
|
||||
{"name": "Accept", "value": "application/json"},
|
||||
{"name": "Version", "value": "2021-07-28"},
|
||||
{"name": "Authorization", "value": "=Bearer {{ $('DATOS API - SUCURSAL').item.json['Token/API'] }}"},
|
||||
{"name": "Content-Type", "value": "application/json"},
|
||||
]},
|
||||
"sendBody": True,
|
||||
"specifyBody": "json",
|
||||
"jsonBody": jsonBody,
|
||||
"options": {},
|
||||
},
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"typeVersion": 4.2,
|
||||
"position": [1448, 176],
|
||||
"id": str(uuid.uuid4()),
|
||||
"name": NEW_NAME,
|
||||
"onError": "continueRegularOutput",
|
||||
"notes": ("Auto-mapeo de seguridad: setea 'ID Oportunidad Sucursal' = id nativo "
|
||||
"de la propia opp de sucursal (del webhook) antes de procesar. "
|
||||
"onError=continue para no romper el sync si falla."),
|
||||
}
|
||||
nodes.append(new_node)
|
||||
|
||||
def setconn(src, dst):
|
||||
conns[src] = {'main': [[{'node': dst, 'type': 'main', 'index': 0}]]}
|
||||
|
||||
# Rama sucursal reordenada: DATOS API - SUCURSAL -> Conseguir CF -> [PUT] -> Buscar Opp -> Obtener info
|
||||
setconn('DATOS API - SUCURSAL', 'Conseguir Custom Fields - Opportunity - SUCURSAL')
|
||||
setconn('Conseguir Custom Fields - Opportunity - SUCURSAL', NEW_NAME)
|
||||
setconn(NEW_NAME, 'Buscar Oportunidad - SUCURSAL')
|
||||
setconn('Buscar Oportunidad - SUCURSAL', 'Obtener info de Oportunidad - SUCURSAL')
|
||||
|
||||
ALLOWED = {'saveExecutionProgress', 'saveManualExecutions', 'saveDataErrorExecution',
|
||||
'saveDataSuccessExecution', 'executionTimeout', 'errorWorkflow', 'timezone', 'executionOrder'}
|
||||
settings = {k: v for k, v in (wf.get('settings') or {}).items() if k in ALLOWED}
|
||||
payload = {'name': wf['name'], 'nodes': nodes, 'connections': conns, 'settings': settings}
|
||||
r = requests.put(f'{base}/api/v1/workflows/{WID}', headers=H, data=json.dumps(payload), timeout=60)
|
||||
print('PUT status:', r.status_code)
|
||||
if r.status_code != 200:
|
||||
print(r.text[:600]); raise SystemExit('PUT fallo')
|
||||
|
||||
wf2 = requests.get(f'{base}/api/v1/workflows/{WID}', headers=H, timeout=30).json()
|
||||
print('versionId nuevo:', wf2.get('versionId'))
|
||||
print('active:', wf2.get('active'))
|
||||
print('total nodos:', len(wf2['nodes']))
|
||||
print('nodo agregado:', any(n.get('name') == NEW_NAME for n in wf2['nodes']))
|
||||
c = wf2['connections']
|
||||
def tgt(s): return [x['node'] for o in c.get(s, {}).get('main', []) for x in (o or [])]
|
||||
print('DATOS API - SUCURSAL ->', tgt('DATOS API - SUCURSAL'))
|
||||
print('Conseguir CF SUCURSAL ->', tgt('Conseguir Custom Fields - Opportunity - SUCURSAL'))
|
||||
print(NEW_NAME, '->', tgt(NEW_NAME))
|
||||
print('Buscar Oportunidad - SUCURSAL ->', tgt('Buscar Oportunidad - SUCURSAL'))
|
||||
@@ -0,0 +1,201 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""Endurece el workflow de sync de opps (Cfgwp0bOtDW8zuKW) con idempotencia
|
||||
GLOBAL via mapeo Baserow, para que NO vuelva a crear replicas duplicadas en
|
||||
Marca cuando la identidad del contacto es ambigua.
|
||||
|
||||
Problema (caso 2026-05-30): el nodo 'Decidir Match (Create vs Update)' busca la
|
||||
opp existente SOLO entre las opps del contacto resuelto ese run. Si resuelve un
|
||||
contacto distinto al de la opp original (mismo telefono/nombre variante), no la
|
||||
encuentra -> CREATE -> duplicado (descuadre positivo Marca>Sucursales).
|
||||
|
||||
Fix:
|
||||
1. Nuevo nodo Baserow 'Buscar Mapeo Opp - Baserow' (getAll, tabla 754,
|
||||
filtro id_opp_sucursal == id de la opp de sucursal). alwaysOutputData=true
|
||||
+ onError=continueRegularOutput (si falla o no hay match, sigue el flujo).
|
||||
2. Se inserta EN SERIE: Set Contact ID Resuelto -> [Buscar Mapeo Opp] ->
|
||||
Buscar Oportunidades del Contacto - MARCA -> Decidir Match. Las refs por
|
||||
nombre ($('NodoX')) de los nodos siguientes se mantienen intactas.
|
||||
3. 'Decidir Match' reescrito: si el mapeo Baserow tiene id_opp_marca -> UPDATE
|
||||
esa opp (global, independiente del contacto); si no, cae al match por CF
|
||||
entre las opps del contacto (logica previa); si nada -> CREATE.
|
||||
|
||||
La frescura del mapeo (opps nuevas) la cubre el backfill idempotente agendado
|
||||
(scripts/backfill_baserow_opp_mapping.py --table-id 754), no un nodo create
|
||||
fragil dentro del flujo de produccion.
|
||||
|
||||
Dry-run por defecto (dump a n8n/dryrun_*.json); --apply para PUT real.
|
||||
"""
|
||||
import argparse
|
||||
import os
|
||||
import sys
|
||||
import uuid
|
||||
|
||||
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
|
||||
from scripts.n8n_workflow_lib import load_credentials, N8NClient
|
||||
|
||||
WID = "Cfgwp0bOtDW8zuKW"
|
||||
BASEROW_DB = 63
|
||||
MAPPING_TABLE = 754
|
||||
FIELD_ID_OPP_SUCURSAL = 7280 # campo "id_opp_sucursal" (primario) en tabla 754
|
||||
BASEROW_CRED = {"baserowApi": {"id": "LZztQ3WMpzXjSTIH", "name": "Baserow account"}}
|
||||
|
||||
NAME_LOOKUP = "Buscar Mapeo Opp - Baserow"
|
||||
SRC = "Set Contact ID Resuelto"
|
||||
NEXT = "Buscar Oportunidades del Contacto - MARCA"
|
||||
DECIDE = "Decidir Match (Create vs Update)"
|
||||
|
||||
NEW_DECIDE_CODE = """// DECISION (Create vs Update) con idempotencia GLOBAL via mapeo Baserow (tabla 754).
|
||||
// (1) Si el mapeo id_opp_sucursal -> id_opp_marca existe -> UPDATE esa opp de Marca,
|
||||
// independiente del contacto resuelto (evita duplicados por contacto ambiguo).
|
||||
// (2) Si no hay mapeo, match por CF entre las opps del contacto resuelto.
|
||||
// (3) Si nada -> CREATE.
|
||||
const result = $input.first().json;
|
||||
const opportunities = result.opportunities || [];
|
||||
const sucursalOppId = $('Datos de Lead').first().json.Oportunidad.opportunity_id;
|
||||
|
||||
let action = 'CREATE';
|
||||
let opportunityId = null;
|
||||
let matchReason = '';
|
||||
|
||||
// (1) Mapeo Baserow global
|
||||
let baserowRows = [];
|
||||
try { baserowRows = $('Buscar Mapeo Opp - Baserow').all().map(i => i.json); } catch (e) { baserowRows = []; }
|
||||
const mapped = baserowRows.find(r =>
|
||||
String(r.id_opp_sucursal || '') === String(sucursalOppId) && String(r.id_opp_marca || '').trim()
|
||||
);
|
||||
|
||||
if (mapped) {
|
||||
action = 'UPDATE';
|
||||
opportunityId = String(mapped.id_opp_marca).trim();
|
||||
matchReason = 'Match por mapeo Baserow (global, tabla 754)';
|
||||
} else {
|
||||
// (2) match por CF entre las opps del contacto resuelto
|
||||
const match = opportunities.find(o =>
|
||||
(o.customFields || []).some(cf => {
|
||||
const v = cf.fieldValueString ?? cf.fieldValue ?? '';
|
||||
return String(v) === String(sucursalOppId);
|
||||
})
|
||||
);
|
||||
if (match) {
|
||||
action = 'UPDATE';
|
||||
opportunityId = match.id;
|
||||
matchReason = 'Match por ID Oportunidad Sucursal (contacto)';
|
||||
} else {
|
||||
action = 'CREATE';
|
||||
matchReason = 'Sin match (Baserow ni contacto) -> CREATE (contacto tiene ' + opportunities.length + ' opps)';
|
||||
}
|
||||
}
|
||||
|
||||
return [{
|
||||
json: {
|
||||
action,
|
||||
opportunityId,
|
||||
matchReason,
|
||||
sucursalOppId,
|
||||
contactId: $('Set Contact ID Resuelto').first().json.contactId,
|
||||
totalOppsContacto: opportunities.length
|
||||
}
|
||||
}];
|
||||
"""
|
||||
|
||||
|
||||
def build_lookup_node():
|
||||
return {
|
||||
"parameters": {
|
||||
"databaseId": BASEROW_DB,
|
||||
"tableId": MAPPING_TABLE,
|
||||
"limit": 1,
|
||||
"additionalOptions": {
|
||||
"filters": {
|
||||
"fields": [
|
||||
{
|
||||
"field": FIELD_ID_OPP_SUCURSAL,
|
||||
"value": "={{ $('Datos de Lead').item.json.Oportunidad.opportunity_id }}",
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
},
|
||||
"type": "n8n-nodes-base.baserow",
|
||||
"typeVersion": 1.1,
|
||||
"position": [4200, 1140],
|
||||
"id": str(uuid.uuid4()),
|
||||
"name": NAME_LOOKUP,
|
||||
"credentials": BASEROW_CRED,
|
||||
"alwaysOutputData": True,
|
||||
"onError": "continueRegularOutput",
|
||||
"notes": (
|
||||
"Idempotencia global: busca en la tabla 754 el mapeo id_opp_sucursal->"
|
||||
"id_opp_marca. Si existe, 'Decidir Match' hara UPDATE de esa opp aunque "
|
||||
"el contacto resuelto sea otro. alwaysOutputData + onError para no romper "
|
||||
"el flujo si no hay match o si Baserow falla."
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
def parse_args():
|
||||
ap = argparse.ArgumentParser()
|
||||
ap.add_argument("--apply", action="store_true",
|
||||
help="Aplicar PUT real (default: dry-run a n8n/dryrun_*.json)")
|
||||
return ap.parse_args()
|
||||
|
||||
|
||||
def main():
|
||||
args = parse_args()
|
||||
client = N8NClient(*load_credentials())
|
||||
|
||||
print(f"[1/6] GET workflow {WID}")
|
||||
wf = client.get_workflow(WID)
|
||||
print(f" versionId previo: {wf.get('versionId')} | active: {wf.get('active')} | nodos: {len(wf.get('nodes') or [])}")
|
||||
|
||||
existing = {n["name"] for n in wf.get("nodes") or []}
|
||||
for needed in [SRC, NEXT, DECIDE, "Datos de Lead"]:
|
||||
if needed not in existing:
|
||||
print(f"\nERROR: falta nodo requerido {needed!r}. Abortando.")
|
||||
sys.exit(2)
|
||||
if NAME_LOOKUP in existing:
|
||||
print(f"\nERROR: nodo {NAME_LOOKUP!r} ya existe. Abortando para no duplicar.")
|
||||
sys.exit(2)
|
||||
|
||||
print(f"\n[2/6] Backup fresco")
|
||||
_, bpath = client.backup_workflow(WID, label="pre_baserow_opp_idempotency")
|
||||
print(f" backup -> {bpath}")
|
||||
|
||||
print(f"\n[3/6] Inyectar nodo lookup Baserow")
|
||||
client.add_node(wf, build_lookup_node())
|
||||
print(f" + {NAME_LOOKUP}")
|
||||
|
||||
print(f"\n[4/6] Reescribir codigo de '{DECIDE}'")
|
||||
decide = client.find_node(wf, DECIDE)
|
||||
decide["parameters"]["jsCode"] = NEW_DECIDE_CODE
|
||||
print(" jsCode actualizado (chequeo Baserow -> fallback contacto -> CREATE)")
|
||||
|
||||
print(f"\n[5/6] Reconectar grafo")
|
||||
conns = wf["connections"]
|
||||
# SRC -> LOOKUP (antes SRC -> NEXT)
|
||||
conns[SRC] = {"main": [[{"node": NAME_LOOKUP, "type": "main", "index": 0}]]}
|
||||
# LOOKUP -> NEXT
|
||||
conns[NAME_LOOKUP] = {"main": [[{"node": NEXT, "type": "main", "index": 0}]]}
|
||||
# NEXT -> DECIDE se mantiene (no se toca)
|
||||
print(f" {SRC} -> {NAME_LOOKUP} -> {NEXT} -> {DECIDE}")
|
||||
|
||||
print(f"\n[6/6] {'APPLY' if args.apply else 'DRY-RUN'} put_workflow")
|
||||
result = client.put_workflow(WID, wf, dry_run=not args.apply)
|
||||
if not args.apply:
|
||||
print(f" dry-run dump -> {result}")
|
||||
print("\nLISTO. Revisa el JSON. Si todo bien, corre con --apply.")
|
||||
return
|
||||
|
||||
new_wf = client.verify_post(WID, expected_node_names=[NAME_LOOKUP], prev_version_id=wf.get("versionId"))
|
||||
print(f" versionId nuevo: {new_wf.get('versionId')}")
|
||||
if not new_wf.get("active"):
|
||||
print(" reactivando workflow...")
|
||||
client.activate(WID)
|
||||
new_wf = client.get_workflow(WID)
|
||||
print(f" active final: {new_wf.get('active')} | nodos: {len(new_wf.get('nodes') or [])}")
|
||||
print("\nOK. Workflow endurecido. Corre el test E2E para validar UPDATE vs CREATE.")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,183 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""Cablea el UPSERT en tiempo real del mapeo Baserow (tabla 754) tras crear/
|
||||
actualizar la opp en Marca, en el workflow Cfgwp0bOtDW8zuKW.
|
||||
|
||||
Contexto: el lookup Baserow + 'Decidir Match' ya evitan duplicados cuando el
|
||||
mapeo id_opp_sucursal->id_opp_marca existe. PERO las salidas de
|
||||
'Crear Oportunidad - MARCA' y 'Actualizar Oportunidad - MARCA (v2)' estaban
|
||||
VACIAS -> el mapeo solo se refrescaba con el backfill agendado (cada 30 min).
|
||||
En esa ventana, una re-ejecucion del mismo opp podia volver a no encontrar el
|
||||
mapeo y, si el contacto era ambiguo, crear un duplicado.
|
||||
|
||||
Fix (aditivo, 2 nodos, create-only condicional -> minimo riesgo):
|
||||
- 'Preparar Upsert Mapeo' [code]: decide si hay que escribir el mapeo. Solo
|
||||
escribe cuando NO existe aun (action CREATE, o UPDATE resuelto por contacto;
|
||||
NUNCA cuando 'Decidir Match' ya lo resolvio via Baserow -> la fila ya existe,
|
||||
no duplicar). Calcula id_opp_marca segun el camino. Si no hace falta -> [].
|
||||
- 'Crear Mapeo - Baserow' [baserow create]: inserta la fila {id_opp_sucursal,
|
||||
id_opp_marca, location_id_sucursal, updated_at}.
|
||||
- Ambos con onError=continueRegularOutput: si Baserow falla, NO rompe la
|
||||
replicacion (la opp ya quedo creada/actualizada aguas arriba; el backfill
|
||||
completara el mapeo despues).
|
||||
|
||||
Idempotente: el backfill verifica por id_opp_sucursal antes de crear, asi que
|
||||
una fila creada por este upsert no se duplica luego.
|
||||
|
||||
Dry-run por defecto (dump a n8n/dryrun_*.json); --apply para PUT real.
|
||||
"""
|
||||
import argparse
|
||||
import os
|
||||
import sys
|
||||
import uuid
|
||||
|
||||
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
from scripts.n8n_workflow_lib import load_credentials, N8NClient
|
||||
|
||||
WID = "Cfgwp0bOtDW8zuKW"
|
||||
BASEROW_DB = 63
|
||||
MAPPING_TABLE = 754
|
||||
F_ID_OPP_SUCURSAL = 7280
|
||||
F_ID_OPP_MARCA = 7283
|
||||
F_LOCATION_SUCURSAL = 7284
|
||||
F_UPDATED_AT = 7285
|
||||
BASEROW_CRED = {"baserowApi": {"id": "LZztQ3WMpzXjSTIH", "name": "Baserow account"}}
|
||||
|
||||
NAME_PREP = "Preparar Upsert Mapeo"
|
||||
NAME_CREATE = "Crear Mapeo - Baserow"
|
||||
CREATE_SRC = "Crear Oportunidad - MARCA"
|
||||
UPDATE_SRC = "Actualizar Oportunidad - MARCA (v2)"
|
||||
DECIDE = "Decidir Match (Create vs Update)"
|
||||
|
||||
PREP_CODE = """// Prepara el UPSERT del mapeo Baserow (tabla 754) tras CREATE/UPDATE de la opp en Marca.
|
||||
// Solo escribe cuando el mapeo NO existe aun:
|
||||
// - action CREATE -> la fila no existe (el lookup Baserow fallo) -> crear.
|
||||
// - action UPDATE por contacto -> Baserow no la tenia -> crear (idempotencia futura).
|
||||
// - action UPDATE por Baserow -> la fila YA existe -> NO escribir (no duplicar).
|
||||
// Asi una re-ejecucion del mismo opp encuentra el mapeo y hace UPDATE, no CREATE duplicado.
|
||||
const decision = $('Decidir Match (Create vs Update)').first().json;
|
||||
const action = decision.action;
|
||||
const matchReason = decision.matchReason || '';
|
||||
const sucursalOppId = decision.sucursalOppId;
|
||||
|
||||
const matchedByBaserow = matchReason.indexOf('Baserow') !== -1;
|
||||
const needWrite = (action === 'CREATE') || (action === 'UPDATE' && !matchedByBaserow);
|
||||
if (!needWrite) return [];
|
||||
|
||||
// id de la opp en Marca segun el camino
|
||||
let marcaOppId = decision.opportunityId;
|
||||
if (action === 'CREATE') {
|
||||
try {
|
||||
const co = $('Crear Oportunidad - MARCA').first().json;
|
||||
marcaOppId = (co.opportunity && co.opportunity.id) || co.id || marcaOppId;
|
||||
} catch (e) {}
|
||||
}
|
||||
if (!marcaOppId || !sucursalOppId) return [];
|
||||
|
||||
let locationSucursal = '';
|
||||
try { locationSucursal = $('DATOS API - SUCURSAL').first().json['Location ID'] || ''; } catch (e) {}
|
||||
|
||||
return [{ json: { sucursalOppId, marcaOppId, locationSucursal } }];
|
||||
"""
|
||||
|
||||
|
||||
def build_prep_node():
|
||||
return {
|
||||
"parameters": {"jsCode": PREP_CODE},
|
||||
"type": "n8n-nodes-base.code",
|
||||
"typeVersion": 2,
|
||||
"position": [6040, 960],
|
||||
"id": str(uuid.uuid4()),
|
||||
"name": NAME_PREP,
|
||||
"onError": "continueRegularOutput",
|
||||
"notes": ("Decide si escribir el mapeo Baserow tras CREATE/UPDATE. Solo cuando la "
|
||||
"fila no existe (CREATE o UPDATE-por-contacto). onError continue para no "
|
||||
"romper la replicacion."),
|
||||
}
|
||||
|
||||
|
||||
def build_create_node():
|
||||
return {
|
||||
"parameters": {
|
||||
"operation": "create",
|
||||
"databaseId": BASEROW_DB,
|
||||
"tableId": MAPPING_TABLE,
|
||||
"fieldsUi": {
|
||||
"fieldValues": [
|
||||
{"fieldId": F_ID_OPP_SUCURSAL, "fieldValue": "={{ $json.sucursalOppId }}"},
|
||||
{"fieldId": F_ID_OPP_MARCA, "fieldValue": "={{ $json.marcaOppId }}"},
|
||||
{"fieldId": F_LOCATION_SUCURSAL, "fieldValue": "={{ $json.locationSucursal }}"},
|
||||
{"fieldId": F_UPDATED_AT, "fieldValue": "={{ $now.toISO() }}"},
|
||||
]
|
||||
},
|
||||
},
|
||||
"type": "n8n-nodes-base.baserow",
|
||||
"typeVersion": 1,
|
||||
"position": [6280, 960],
|
||||
"id": str(uuid.uuid4()),
|
||||
"name": NAME_CREATE,
|
||||
"credentials": BASEROW_CRED,
|
||||
"onError": "continueRegularOutput",
|
||||
"notes": ("Inserta el mapeo id_opp_sucursal->id_opp_marca en la tabla 754 en tiempo "
|
||||
"real. Idempotente con el backfill (verifica por id_opp_sucursal)."),
|
||||
}
|
||||
|
||||
|
||||
def main():
|
||||
ap = argparse.ArgumentParser()
|
||||
ap.add_argument("--apply", action="store_true", help="PUT real (default: dry-run).")
|
||||
args = ap.parse_args()
|
||||
client = N8NClient(*load_credentials())
|
||||
|
||||
print(f"[1/6] GET workflow {WID}")
|
||||
wf = client.get_workflow(WID)
|
||||
print(f" versionId previo: {wf.get('versionId')} | active: {wf.get('active')} | nodos: {len(wf.get('nodes') or [])}")
|
||||
|
||||
existing = {n["name"] for n in wf.get("nodes") or []}
|
||||
for needed in [CREATE_SRC, UPDATE_SRC, DECIDE]:
|
||||
if needed not in existing:
|
||||
print(f"\nERROR: falta nodo requerido {needed!r}. Abortando.")
|
||||
sys.exit(2)
|
||||
for new in [NAME_PREP, NAME_CREATE]:
|
||||
if new in existing:
|
||||
print(f"\nERROR: nodo {new!r} ya existe. Abortando para no duplicar.")
|
||||
sys.exit(2)
|
||||
|
||||
print("\n[2/6] Backup fresco")
|
||||
_, bpath = client.backup_workflow(WID, label="pre_baserow_opp_upsert")
|
||||
print(f" backup -> {bpath}")
|
||||
|
||||
print("\n[3/6] Inyectar nodos")
|
||||
client.add_node(wf, build_prep_node())
|
||||
client.add_node(wf, build_create_node())
|
||||
print(f" + {NAME_PREP}\n + {NAME_CREATE}")
|
||||
|
||||
print("\n[4/6] Reconectar grafo (aditivo; las salidas estaban vacias)")
|
||||
conns = wf["connections"]
|
||||
conns[CREATE_SRC] = {"main": [[{"node": NAME_PREP, "type": "main", "index": 0}]]}
|
||||
conns[UPDATE_SRC] = {"main": [[{"node": NAME_PREP, "type": "main", "index": 0}]]}
|
||||
conns[NAME_PREP] = {"main": [[{"node": NAME_CREATE, "type": "main", "index": 0}]]}
|
||||
print(f" {CREATE_SRC} -> {NAME_PREP}")
|
||||
print(f" {UPDATE_SRC} -> {NAME_PREP}")
|
||||
print(f" {NAME_PREP} -> {NAME_CREATE}")
|
||||
|
||||
print(f"\n[5/6] {'APPLY' if args.apply else 'DRY-RUN'} put_workflow")
|
||||
result = client.put_workflow(WID, wf, dry_run=not args.apply)
|
||||
if not args.apply:
|
||||
print(f" dry-run dump -> {result}")
|
||||
print("\nLISTO. Revisa el JSON. Si todo bien, corre con --apply.")
|
||||
return
|
||||
|
||||
print("\n[6/6] Verificar")
|
||||
new_wf = client.verify_post(WID, expected_node_names=[NAME_PREP, NAME_CREATE],
|
||||
prev_version_id=wf.get("versionId"))
|
||||
print(f" versionId nuevo: {new_wf.get('versionId')}")
|
||||
if not new_wf.get("active"):
|
||||
print(" reactivando workflow...")
|
||||
client.activate(WID)
|
||||
new_wf = client.get_workflow(WID)
|
||||
print(f" active final: {new_wf.get('active')} | nodos: {len(new_wf.get('nodes') or [])}")
|
||||
print("\nOK. Upsert Baserow en tiempo real cableado (degradacion elegante via onError).")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,268 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""Complementa el workflow n8n [2004] (ddUEORBEtZLzsQF2) para que, al crear un
|
||||
contacto en sucursal, además de rellenar contact.sucursal / contact.tienda,
|
||||
escriba "Canal de Origen" = SUCURSAL y deje el tag de origen correcto, PERO solo
|
||||
cuando el contacto fue creado por un usuario (createdBy.source ∈ {WEB_USER,
|
||||
MOBILE_USER}). Los creados por integración (INTEGRATION) no se tocan.
|
||||
|
||||
Replica en TIEMPO REAL el criterio del batch scripts/fix_branch_user_origin.py.
|
||||
|
||||
Cambios (solo AÑADE, preserva el flujo actual):
|
||||
1. Extiende el Code node "Buscar contact.sucursal y contact.tienda" para
|
||||
resolver también el field "Canal de Origen" (por fieldKey con fallback a
|
||||
nombre) y exponer createdBySource / esUsuario (leído del GET del contacto).
|
||||
2. Tras el PUT actual de sucursal/tienda añade:
|
||||
IF "Creado por usuario" -> [true] PUT Canal de Origen = SUCURSAL
|
||||
-> Tag+ sucursal -> Tag- formulario -> Tag- facebook-ads
|
||||
[false] (fin: no se toca).
|
||||
|
||||
Uso:
|
||||
python n8n/_add_canal_origen_branch.py # dry-run (dumpea payload)
|
||||
python n8n/_add_canal_origen_branch.py --apply # aplica + reactiva
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import sys
|
||||
|
||||
ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
if ROOT_DIR not in sys.path:
|
||||
sys.path.insert(0, ROOT_DIR)
|
||||
|
||||
from scripts.n8n_workflow_lib import ( # noqa: E402
|
||||
AlreadyExistsError,
|
||||
N8NClient,
|
||||
load_credentials,
|
||||
)
|
||||
|
||||
WID = "ddUEORBEtZLzsQF2"
|
||||
|
||||
# Nodos existentes (referencias).
|
||||
CODE_NODE = 'Buscar "contact.sucursal" y "contact.tienda"'
|
||||
GET_CONTACT_NODE = "Obtener Contacto Cuenta Origen - SUCURSAL"
|
||||
PUT_SUCURSAL_NODE = "Actualizar Contacto Cuenta Objetivo - SUCURSAL"
|
||||
VERIFICADOR_NODE = "Buscar Sucursal en Verificador de Sucursales"
|
||||
|
||||
# Nodos nuevos.
|
||||
IF_NODE = "Creado por usuario (Canal de Origen)"
|
||||
PUT_CANAL_NODE = "PUT Canal de Origen = SUCURSAL"
|
||||
TAG_ADD_NODE = "Tag+ sucursal"
|
||||
TAG_RM_FORM_NODE = "Tag- formulario"
|
||||
TAG_RM_FB_NODE = "Tag- facebook-ads"
|
||||
|
||||
# Expresiones reutilizables (referencian nodos upstream por nombre).
|
||||
CONTACT_ID_EXPR = "{{ $('Datos de Lead').item.json.Cliente['Contact ID'] }}"
|
||||
TOKEN_EXPR = "{{ $('" + VERIFICADOR_NODE + "').item.json['SC TOKEN BUCEFALO'] }}"
|
||||
CODE_REF = "$('" + CODE_NODE + "').item.json"
|
||||
|
||||
HEADERS = [
|
||||
{"name": "Accept", "value": "application/json"},
|
||||
{"name": "Version", "value": "2021-07-28"},
|
||||
{"name": "Authorization", "value": "=Bearer " + TOKEN_EXPR},
|
||||
]
|
||||
|
||||
# Code node extendido: preserva sucursal/tienda EXACTO y añade canal + esUsuario.
|
||||
NEW_JSCODE = r"""const customFields = $input.first().json.customFields;
|
||||
|
||||
function findField(key, names) {
|
||||
let f = customFields.find(x => x.fieldKey === key);
|
||||
if (!f) {
|
||||
const wanted = names.map(n => n.toLowerCase().trim());
|
||||
f = customFields.find(x => wanted.includes((x.name || "").toLowerCase().trim()));
|
||||
}
|
||||
return f || null;
|
||||
}
|
||||
|
||||
const sucursalField = findField("contact.sucursal", ["Sucursal"]);
|
||||
const tiendaField = findField("contact.tienda", ["TIENDA", "Tienda"]);
|
||||
const canalField = findField("contact.fuente_de_posible_cliente", ["CANAL DE ORIGEN", "Canal de Origen"]);
|
||||
|
||||
// createdBy.source SOLO viene del GET individual del contacto.
|
||||
const contactResp = $('Obtener Contacto Cuenta Origen - SUCURSAL').item.json;
|
||||
const createdBySource =
|
||||
(contactResp && contactResp.contact && contactResp.contact.createdBy && contactResp.contact.createdBy.source) ||
|
||||
(contactResp && contactResp.createdBy && contactResp.createdBy.source) ||
|
||||
null;
|
||||
const esUsuario = createdBySource === "WEB_USER" || createdBySource === "MOBILE_USER";
|
||||
|
||||
return [
|
||||
{
|
||||
json: {
|
||||
sucursal: {
|
||||
id: sucursalField?.id ?? null,
|
||||
name: sucursalField?.name ?? null,
|
||||
fieldKey: sucursalField?.fieldKey ?? null,
|
||||
picklistOptions: sucursalField?.picklistOptions ?? [],
|
||||
},
|
||||
tienda: {
|
||||
id: tiendaField?.id ?? null,
|
||||
name: tiendaField?.name ?? null,
|
||||
fieldKey: tiendaField?.fieldKey ?? null,
|
||||
picklistOptions: tiendaField?.picklistOptions ?? [],
|
||||
},
|
||||
canal: {
|
||||
id: canalField?.id ?? null,
|
||||
name: canalField?.name ?? null,
|
||||
fieldKey: canalField?.fieldKey ?? null,
|
||||
picklistOptions: canalField?.picklistOptions ?? [],
|
||||
},
|
||||
createdBySource: createdBySource,
|
||||
esUsuario: esUsuario,
|
||||
},
|
||||
},
|
||||
];"""
|
||||
|
||||
|
||||
def http_node(name, position, *, method, body_obj_expr, on_error=False):
|
||||
"""Construye un nodo httpRequest (typeVersion 4.2) con auth Bearer en header."""
|
||||
params = {
|
||||
"method": method,
|
||||
"url": "=https://services.leadconnectorhq.com/contacts/" + CONTACT_ID_EXPR + ("/tags" if method in ("POST", "DELETE") else ""),
|
||||
"sendHeaders": True,
|
||||
"headerParameters": {"parameters": [dict(h) for h in HEADERS]},
|
||||
"sendBody": True,
|
||||
"specifyBody": "json",
|
||||
"jsonBody": body_obj_expr,
|
||||
"options": {"redirect": {"redirect": {}}},
|
||||
}
|
||||
node = {
|
||||
"parameters": params,
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"typeVersion": 4.2,
|
||||
"position": position,
|
||||
"name": name,
|
||||
}
|
||||
if on_error:
|
||||
# Un DELETE de un tag inexistente puede no aplicar; no debe romper el flujo.
|
||||
node["onError"] = "continueRegularOutput"
|
||||
return node
|
||||
|
||||
|
||||
def build_nodes():
|
||||
# PUT Canal de Origen = SUCURSAL (solo este CF; NO toca Fuente de Prospecto).
|
||||
canal_body = (
|
||||
"={\n"
|
||||
' "customFields": [\n'
|
||||
" {\n"
|
||||
' "id": "{{ ' + CODE_REF + '.canal.id }}",\n'
|
||||
' "key": "{{ ' + CODE_REF + '.canal.fieldKey }}",\n'
|
||||
' "field_value": "SUCURSAL"\n'
|
||||
" }\n"
|
||||
" ]\n"
|
||||
"}"
|
||||
)
|
||||
put_canal = {
|
||||
"parameters": {
|
||||
"method": "PUT",
|
||||
"url": "=https://services.leadconnectorhq.com/contacts/" + CONTACT_ID_EXPR,
|
||||
"sendHeaders": True,
|
||||
"headerParameters": {"parameters": [dict(h) for h in HEADERS]},
|
||||
"sendBody": True,
|
||||
"specifyBody": "json",
|
||||
"jsonBody": canal_body,
|
||||
"options": {"redirect": {"redirect": {}}},
|
||||
},
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"typeVersion": 4.2,
|
||||
"position": [940, -120],
|
||||
"name": PUT_CANAL_NODE,
|
||||
}
|
||||
|
||||
if_node = {
|
||||
"parameters": {
|
||||
"conditions": {
|
||||
"options": {"caseSensitive": True, "leftValue": "", "typeValidation": "loose", "version": 3},
|
||||
"conditions": [
|
||||
{
|
||||
"id": "canal-origen-esusuario",
|
||||
"leftValue": "={{ " + CODE_REF + ".esUsuario }}",
|
||||
"rightValue": "",
|
||||
"operator": {"type": "boolean", "operation": "true", "singleValue": True},
|
||||
}
|
||||
],
|
||||
"combinator": "and",
|
||||
},
|
||||
"options": {},
|
||||
},
|
||||
"type": "n8n-nodes-base.if",
|
||||
"typeVersion": 2.3,
|
||||
"position": [720, -120],
|
||||
"name": IF_NODE,
|
||||
}
|
||||
|
||||
tag_add = http_node(TAG_ADD_NODE, [1160, -120], method="POST",
|
||||
body_obj_expr='={\n "tags": ["sucursal"]\n}')
|
||||
tag_rm_form = http_node(TAG_RM_FORM_NODE, [1380, -120], method="DELETE",
|
||||
body_obj_expr='={\n "tags": ["formulario"]\n}', on_error=True)
|
||||
tag_rm_fb = http_node(TAG_RM_FB_NODE, [1600, -120], method="DELETE",
|
||||
body_obj_expr='={\n "tags": ["facebook-ads"]\n}', on_error=True)
|
||||
return if_node, put_canal, tag_add, tag_rm_form, tag_rm_fb
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Añade la rama Canal de Origen al workflow n8n [2004].")
|
||||
parser.add_argument("--apply", action="store_true", help="Aplica el PUT real (sin esto: dry-run).")
|
||||
args = parser.parse_args()
|
||||
|
||||
client = N8NClient(*load_credentials())
|
||||
wf, backup_path = client.backup_workflow(WID, label="canal_origen")
|
||||
prev_version = wf.get("versionId")
|
||||
print(f"Workflow: {wf.get('name')}")
|
||||
print(f" active={wf.get('active')} nodes={len(wf.get('nodes') or [])} versionId={prev_version}")
|
||||
print(f" backup -> {backup_path}")
|
||||
|
||||
# Idempotencia: si ya están los nodos nuevos, no re-aplicar.
|
||||
for nm in (IF_NODE, PUT_CANAL_NODE, TAG_ADD_NODE):
|
||||
if client.find_node(wf, nm) is not None:
|
||||
raise SystemExit(f"El nodo {nm!r} ya existe; el complemento ya fue aplicado. Nada que hacer.")
|
||||
|
||||
# 1. Extender el Code node.
|
||||
code_node = client.find_node(wf, CODE_NODE)
|
||||
if code_node is None:
|
||||
raise SystemExit(f"No se encontró el Code node {CODE_NODE!r}.")
|
||||
code_node["parameters"]["jsCode"] = NEW_JSCODE
|
||||
print(f" Code node {CODE_NODE!r}: jsCode extendido (canal + esUsuario).")
|
||||
|
||||
# 2. Añadir nodos nuevos.
|
||||
if_node, put_canal, tag_add, tag_rm_form, tag_rm_fb = build_nodes()
|
||||
for n in (if_node, put_canal, tag_add, tag_rm_form, tag_rm_fb):
|
||||
client.assert_idempotent(wf, n["name"])
|
||||
client.add_node(wf, n)
|
||||
print(" Nodos añadidos: IF + PUT canal + 3 de tags.")
|
||||
|
||||
# 3. Conexiones: PUT sucursal -> IF -> [true] PUT canal -> tag+ -> tag- form -> tag- fb.
|
||||
client.set_connection(wf, PUT_SUCURSAL_NODE, IF_NODE) # main:0
|
||||
client.set_connection(wf, IF_NODE, PUT_CANAL_NODE, output_index=0) # rama true
|
||||
# rama false (index 1): sin destino (fin).
|
||||
client.set_connection(wf, PUT_CANAL_NODE, TAG_ADD_NODE)
|
||||
client.set_connection(wf, TAG_ADD_NODE, TAG_RM_FORM_NODE)
|
||||
client.set_connection(wf, TAG_RM_FORM_NODE, TAG_RM_FB_NODE)
|
||||
print(" Conexiones cableadas.")
|
||||
|
||||
expected = [IF_NODE, PUT_CANAL_NODE, TAG_ADD_NODE, TAG_RM_FORM_NODE, TAG_RM_FB_NODE]
|
||||
|
||||
if not args.apply:
|
||||
res = client.put_workflow(WID, wf, dry_run=True)
|
||||
print(f"\nDRY-RUN. Payload -> {res['path']} ({res['node_count']} nodos).")
|
||||
print("Revisa el JSON y vuelve a correr con --apply para aplicar.")
|
||||
return
|
||||
|
||||
was_active = bool(wf.get("active"))
|
||||
if was_active:
|
||||
try:
|
||||
client.deactivate(WID)
|
||||
print(" Workflow desactivado para PUT estructural.")
|
||||
except Exception as exc:
|
||||
print(f" WARN al desactivar: {exc}")
|
||||
client.put_workflow(WID, wf, dry_run=False)
|
||||
print(" PUT aplicado.")
|
||||
if was_active:
|
||||
client.activate(WID)
|
||||
print(" Workflow reactivado.")
|
||||
client.verify_post(WID, expected_node_names=expected, prev_version_id=prev_version)
|
||||
print("\nOK: complemento aplicado y verificado. Backup en:", backup_path)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,203 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""Fallback de autoenlace para el workflow Sucursal->Marca (x4DqZ5FtSc43tdzB).
|
||||
|
||||
Inserta entre 'Mapear Custom Fields Cuenta Origen - SUCURSAL' y
|
||||
'DATOS CUENTA OBJETIVO' un IF + PUT condicional:
|
||||
- Si el CF 'contact.id_contacto_sucursal' ya == contact.id -> bypass directo.
|
||||
- Si NO -> PUT autoenlace en la sucursal antes de continuar.
|
||||
|
||||
Defensivo contra automatizaciones nativas de GHL que crean contactos sin
|
||||
disparar nuestro fill. onError=continueRegularOutput para no romper el sync
|
||||
si el PUT falla.
|
||||
|
||||
Modo dry-run por defecto (dumpea n8n/dryrun_*.json sin tocar la API).
|
||||
Pasar --apply para PUT real + reactivar.
|
||||
"""
|
||||
import argparse
|
||||
import sys
|
||||
import os
|
||||
|
||||
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
|
||||
from scripts.n8n_workflow_lib import load_credentials, N8NClient
|
||||
import uuid
|
||||
|
||||
WID = "x4DqZ5FtSc43tdzB"
|
||||
|
||||
NAME_IF = "ya esta el CF id_contacto_sucursal"
|
||||
NAME_PUT = "Autoenlace CF id_contacto_sucursal - SUCURSAL"
|
||||
|
||||
SRC = "Mapear Custom Fields Cuenta Origen - SUCURSAL"
|
||||
DST = "DATOS CUENTA OBJETIVO"
|
||||
|
||||
|
||||
def build_if_node():
|
||||
return {
|
||||
"parameters": {
|
||||
"conditions": {
|
||||
"options": {
|
||||
"caseSensitive": True,
|
||||
"leftValue": "",
|
||||
"typeValidation": "strict",
|
||||
"version": 2,
|
||||
},
|
||||
"conditions": [
|
||||
{
|
||||
"id": str(uuid.uuid4()),
|
||||
"leftValue": "={{ $json['contact.id_contacto_sucursal'] }}",
|
||||
"rightValue": "={{ $json.id }}",
|
||||
"operator": {
|
||||
"type": "string",
|
||||
"operation": "equals",
|
||||
"name": "filter.operator.equals",
|
||||
},
|
||||
}
|
||||
],
|
||||
"combinator": "and",
|
||||
},
|
||||
"options": {},
|
||||
},
|
||||
"type": "n8n-nodes-base.if",
|
||||
"typeVersion": 2.2,
|
||||
"position": [1760, -288],
|
||||
"id": str(uuid.uuid4()),
|
||||
"name": NAME_IF,
|
||||
"notes": (
|
||||
"Fallback defensivo. Si el CF 'contact.id_contacto_sucursal' ya == contact.id "
|
||||
"(estado normal poblado por fill_contact_id_sucursal.py), pasa directo. "
|
||||
"Si esta vacio o distinto (creacion por automatizacion nativa de GHL que se "
|
||||
"salto nuestro fill), va a la rama PUT para autoenlazar antes de tocar Marca."
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
def build_put_node():
|
||||
json_body = (
|
||||
"={{ JSON.stringify({ customFields: [ { "
|
||||
"id: $('Conseguir Custom Cuenta Origen- SUCURSAL').first().json.customFields"
|
||||
".find(f => f.fieldKey === 'contact.id_contacto_sucursal')?.id, "
|
||||
"key: 'contact.id_contacto_sucursal', "
|
||||
"field_value: $('Mapear Custom Fields Cuenta Origen - SUCURSAL').item.json.id"
|
||||
" } ] }) }}"
|
||||
)
|
||||
return {
|
||||
"parameters": {
|
||||
"method": "PUT",
|
||||
"url": (
|
||||
"=https://services.leadconnectorhq.com/contacts/"
|
||||
"{{ $('Mapear Custom Fields Cuenta Origen - SUCURSAL').item.json.id }}"
|
||||
),
|
||||
"sendHeaders": True,
|
||||
"headerParameters": {
|
||||
"parameters": [
|
||||
{"name": "Accept", "value": "application/json"},
|
||||
{"name": "Version", "value": "2021-07-28"},
|
||||
{
|
||||
"name": "Authorization",
|
||||
"value": (
|
||||
"=Bearer "
|
||||
"{{ $('Obtener Info de cuenta origen - SUCURSAL').item.json.API_token }}"
|
||||
),
|
||||
},
|
||||
{"name": "Content-Type", "value": "application/json"},
|
||||
]
|
||||
},
|
||||
"sendBody": True,
|
||||
"specifyBody": "json",
|
||||
"jsonBody": json_body,
|
||||
"options": {},
|
||||
},
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"typeVersion": 4.2,
|
||||
"position": [1936, -192],
|
||||
"id": str(uuid.uuid4()),
|
||||
"name": NAME_PUT,
|
||||
"onError": "continueRegularOutput",
|
||||
"notes": (
|
||||
"Autoenlace de seguridad: setea CF 'contact.id_contacto_sucursal' = "
|
||||
"contact.id propio en la sucursal. Solo se ejecuta si el IF previo "
|
||||
"detecto que el CF estaba vacio o distinto. onError=continue para no "
|
||||
"romper el sync si el PUT falla."
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
def parse_args():
|
||||
ap = argparse.ArgumentParser()
|
||||
ap.add_argument("--apply", action="store_true",
|
||||
help="Aplicar PUT real (default: dry-run a n8n/dryrun_*.json)")
|
||||
return ap.parse_args()
|
||||
|
||||
|
||||
def main():
|
||||
args = parse_args()
|
||||
api_key, base_url = load_credentials()
|
||||
client = N8NClient(api_key, base_url)
|
||||
|
||||
print(f"[1/6] GET workflow {WID}")
|
||||
wf = client.get_workflow(WID)
|
||||
print(f" versionId previo: {wf.get('versionId')}")
|
||||
print(f" active : {wf.get('active')}")
|
||||
print(f" total nodos : {len(wf.get('nodes') or [])}")
|
||||
|
||||
existing = {n["name"] for n in wf.get("nodes") or []}
|
||||
if NAME_IF in existing or NAME_PUT in existing:
|
||||
print(f"\nERROR: los nodos ya existen ({NAME_IF!r} o {NAME_PUT!r}). Abortando.")
|
||||
sys.exit(2)
|
||||
if SRC not in existing:
|
||||
print(f"\nERROR: no encontre el nodo source {SRC!r}. Abortando.")
|
||||
sys.exit(2)
|
||||
if DST not in existing:
|
||||
print(f"\nERROR: no encontre el nodo destino {DST!r}. Abortando.")
|
||||
sys.exit(2)
|
||||
|
||||
print(f"\n[2/6] Backup fresco")
|
||||
_, bpath = client.backup_workflow(WID, label="pre_fallback_autoenlace")
|
||||
print(f" backup -> {bpath}")
|
||||
|
||||
print(f"\n[3/6] Inyectar nodos IF + PUT")
|
||||
if_node = build_if_node()
|
||||
put_node = build_put_node()
|
||||
client.add_node(wf, if_node)
|
||||
client.add_node(wf, put_node)
|
||||
print(f" IF : {NAME_IF}")
|
||||
print(f" PUT: {NAME_PUT}")
|
||||
|
||||
print(f"\n[4/6] Reconectar grafo")
|
||||
# Reemplaza SRC -> DST por SRC -> IF
|
||||
client.insert_between(wf, SRC, NAME_IF, DST)
|
||||
# IF.output(0)=true (CF ya ok) -> DST; IF.output(1)=false (CF vacio/distinto) -> PUT -> DST
|
||||
client.branch_if(wf, NAME_IF, true_target=DST, false_target=NAME_PUT)
|
||||
# PUT -> DST (convergencia)
|
||||
conns = wf["connections"]
|
||||
conns[NAME_PUT] = {"main": [[{"node": DST, "type": "main", "index": 0}]]}
|
||||
print(f" {SRC} -> {NAME_IF}")
|
||||
print(f" {NAME_IF} [true] -> {DST}")
|
||||
print(f" {NAME_IF} [false] -> {NAME_PUT}")
|
||||
print(f" {NAME_PUT} -> {DST}")
|
||||
|
||||
print(f"\n[5/6] {'APPLY' if args.apply else 'DRY-RUN'} put_workflow")
|
||||
result = client.put_workflow(WID, wf, dry_run=not args.apply)
|
||||
if not args.apply:
|
||||
print(f" dry-run dump -> {result}")
|
||||
print("\nLISTO. Revisa el JSON. Si todo bien, corre con --apply.")
|
||||
return
|
||||
|
||||
print(f"\n[6/6] Verificar y reactivar")
|
||||
new_wf = client.verify_post(
|
||||
WID,
|
||||
expected_node_names=[NAME_IF, NAME_PUT],
|
||||
prev_version_id=wf.get("versionId"),
|
||||
)
|
||||
print(f" versionId nuevo: {new_wf.get('versionId')}")
|
||||
if not new_wf.get("active"):
|
||||
print(" workflow quedo inactivo tras PUT; reactivando...")
|
||||
client.activate(WID)
|
||||
new_wf = client.get_workflow(WID)
|
||||
print(f" active final : {new_wf.get('active')}")
|
||||
print(f" total nodos : {len(new_wf.get('nodes') or [])}")
|
||||
print("\nOK.")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,265 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""Fallback post-UPDATE para el workflow Marca->Sucursal V2 (4UMRwxJdHFfOGHBp).
|
||||
|
||||
Cierra el gap del path UPDATE-via-cascada: cuando el contacto Marca llega sin
|
||||
CF id_contacto_sucursal poblado, va por cascada (mail/telefono/nombre), encuentra
|
||||
contacto sucursal existente, lo actualiza... y el CF Marca queda vacio para
|
||||
siempre (la siguiente ejecucion volvera por cascada).
|
||||
|
||||
Inserta tras 'Actualizar Contacto Cuenta Objetivo - SUCURSAL':
|
||||
|
||||
IF '¿cfValue de Marca estaba vacio?' (eval: cfValue extraido al inicio)
|
||||
true -> PUT 1 (CF en MARCA = id sucursal encontrado)
|
||||
-> PUT 2 (CF en SUCURSAL = autoref)
|
||||
false -> (fin, ya estaba enlazado)
|
||||
|
||||
Defensivo: onError=continueRegularOutput en ambos PUTs.
|
||||
|
||||
Modo dry-run por defecto; --apply para PUT real + reactivar.
|
||||
"""
|
||||
import argparse
|
||||
import sys
|
||||
import os
|
||||
import uuid
|
||||
|
||||
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
|
||||
from scripts.n8n_workflow_lib import load_credentials, N8NClient
|
||||
|
||||
WID = "4UMRwxJdHFfOGHBp"
|
||||
BRAND_CF_ID = "E6lI9ykWhqpj7Pmi7Qd3" # CF id_contacto_sucursal en Marca (hardcoded)
|
||||
|
||||
NAME_IF = "cfValue Marca estaba vacio"
|
||||
NAME_PUT_MARCA = "Autoenlace post-UPDATE: CF en MARCA"
|
||||
NAME_PUT_SUC = "Autoenlace post-UPDATE: CF en SUCURSAL (autoref)"
|
||||
|
||||
# Nodo terminal donde nos enganchamos
|
||||
SRC_TERMINAL = "Actualizar Contacto Cuenta Objetivo - SUCURSAL"
|
||||
|
||||
|
||||
def build_if_node():
|
||||
return {
|
||||
"parameters": {
|
||||
"conditions": {
|
||||
"options": {
|
||||
"caseSensitive": True,
|
||||
"leftValue": "",
|
||||
"typeValidation": "loose",
|
||||
"version": 3,
|
||||
},
|
||||
"conditions": [
|
||||
{
|
||||
"id": str(uuid.uuid4()),
|
||||
"leftValue": "={{ $('Extraer id_contacto_sucursal de Marca').item.json.cfValue }}",
|
||||
"rightValue": "",
|
||||
"operator": {
|
||||
"type": "string",
|
||||
"operation": "empty",
|
||||
"singleValue": True,
|
||||
},
|
||||
}
|
||||
],
|
||||
"combinator": "and",
|
||||
},
|
||||
"options": {},
|
||||
},
|
||||
"type": "n8n-nodes-base.if",
|
||||
"typeVersion": 2.2,
|
||||
"position": [3984, 480],
|
||||
"id": str(uuid.uuid4()),
|
||||
"name": NAME_IF,
|
||||
"notes": (
|
||||
"Fallback post-UPDATE: si el cfValue extraido al inicio estaba vacio, "
|
||||
"entonces la cascada nos trajo aqui. Autoenlazamos ambos lados para "
|
||||
"que la proxima ejecucion vaya por la ruta deterministica."
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
def build_put_marca():
|
||||
json_body = (
|
||||
'={\n'
|
||||
' "customFields": [\n'
|
||||
' {\n'
|
||||
f' "id": "{BRAND_CF_ID}",\n'
|
||||
' "key": "contact.id_contacto_sucursal",\n'
|
||||
' "field_value": "{{ $(\'Obtener datos completos de Contacto objetivo - SUCURSAL\').item.json.id }}"\n'
|
||||
' }\n'
|
||||
' ]\n'
|
||||
'}'
|
||||
)
|
||||
return {
|
||||
"parameters": {
|
||||
"method": "PUT",
|
||||
"url": (
|
||||
"=https://services.leadconnectorhq.com/contacts/"
|
||||
"{{ $('Webhook').item.json.body.contact_id }}"
|
||||
),
|
||||
"sendHeaders": True,
|
||||
"headerParameters": {
|
||||
"parameters": [
|
||||
{"name": "Accept", "value": "application/json"},
|
||||
{"name": "Version", "value": "2021-07-28"},
|
||||
{
|
||||
"name": "Authorization",
|
||||
"value": (
|
||||
"=Bearer "
|
||||
"{{ $('Datos API Cuenta Origen').item.json['Token/API'] }}"
|
||||
),
|
||||
},
|
||||
{"name": "Content-Type", "value": "application/json"},
|
||||
]
|
||||
},
|
||||
"sendBody": True,
|
||||
"specifyBody": "json",
|
||||
"jsonBody": json_body,
|
||||
"options": {"redirect": {"redirect": {}}},
|
||||
},
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"typeVersion": 4.2,
|
||||
"position": [4176, 384],
|
||||
"id": str(uuid.uuid4()),
|
||||
"name": NAME_PUT_MARCA,
|
||||
"onError": "continueRegularOutput",
|
||||
"notes": "Setea CF id_contacto_sucursal en MARCA = id del contacto sucursal encontrado.",
|
||||
}
|
||||
|
||||
|
||||
def build_put_sucursal():
|
||||
json_body = (
|
||||
'={{ JSON.stringify({ customFields: [ { '
|
||||
"id: $('Conseguir Custom Cuenta objetivo - SUCURSAL').first().json.customFields"
|
||||
".find(f => f.fieldKey === 'contact.id_contacto_sucursal')?.id, "
|
||||
"key: 'contact.id_contacto_sucursal', "
|
||||
"field_value: $('Obtener datos completos de Contacto objetivo - SUCURSAL').item.json.id"
|
||||
' } ] }) }}'
|
||||
)
|
||||
return {
|
||||
"parameters": {
|
||||
"method": "PUT",
|
||||
"url": (
|
||||
"=https://services.leadconnectorhq.com/contacts/"
|
||||
"{{ $('Obtener datos completos de Contacto objetivo - SUCURSAL').item.json.id }}"
|
||||
),
|
||||
"sendHeaders": True,
|
||||
"headerParameters": {
|
||||
"parameters": [
|
||||
{"name": "Accept", "value": "application/json"},
|
||||
{"name": "Version", "value": "2021-07-28"},
|
||||
{
|
||||
"name": "Authorization",
|
||||
"value": (
|
||||
"=Bearer "
|
||||
"{{ $('Datos API Cuenta objetivo - SUCURSAL').item.json['Token/API'] }}"
|
||||
),
|
||||
},
|
||||
{"name": "Content-Type", "value": "application/json"},
|
||||
]
|
||||
},
|
||||
"sendBody": True,
|
||||
"specifyBody": "json",
|
||||
"jsonBody": json_body,
|
||||
"options": {"redirect": {"redirect": {}}},
|
||||
},
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"typeVersion": 4.2,
|
||||
"position": [4368, 384],
|
||||
"id": str(uuid.uuid4()),
|
||||
"name": NAME_PUT_SUC,
|
||||
"onError": "continueRegularOutput",
|
||||
"notes": "Autoref: setea CF en sucursal = su propio contact.id.",
|
||||
}
|
||||
|
||||
|
||||
def parse_args():
|
||||
ap = argparse.ArgumentParser()
|
||||
ap.add_argument("--apply", action="store_true",
|
||||
help="Aplicar PUT real (default: dry-run a n8n/dryrun_*.json)")
|
||||
return ap.parse_args()
|
||||
|
||||
|
||||
def main():
|
||||
args = parse_args()
|
||||
api_key, base_url = load_credentials()
|
||||
client = N8NClient(api_key, base_url)
|
||||
|
||||
print(f"[1/6] GET workflow {WID}")
|
||||
wf = client.get_workflow(WID)
|
||||
print(f" versionId previo: {wf.get('versionId')}")
|
||||
print(f" active : {wf.get('active')}")
|
||||
print(f" total nodos : {len(wf.get('nodes') or [])}")
|
||||
|
||||
existing = {n["name"] for n in wf.get("nodes") or []}
|
||||
for needed_existing in [SRC_TERMINAL, "Extraer id_contacto_sucursal de Marca",
|
||||
"Webhook", "Datos API Cuenta Origen",
|
||||
"Datos API Cuenta objetivo - SUCURSAL",
|
||||
"Conseguir Custom Cuenta objetivo - SUCURSAL",
|
||||
"Obtener datos completos de Contacto objetivo - SUCURSAL"]:
|
||||
if needed_existing not in existing:
|
||||
print(f"\nERROR: falta nodo requerido {needed_existing!r}. Abortando.")
|
||||
sys.exit(2)
|
||||
for new_name in [NAME_IF, NAME_PUT_MARCA, NAME_PUT_SUC]:
|
||||
if new_name in existing:
|
||||
print(f"\nERROR: nodo {new_name!r} ya existe. Abortando para no duplicar.")
|
||||
sys.exit(2)
|
||||
|
||||
print(f"\n[2/6] Backup fresco")
|
||||
_, bpath = client.backup_workflow(WID, label="pre_fallback_post_update")
|
||||
print(f" backup -> {bpath}")
|
||||
|
||||
print(f"\n[3/6] Inyectar IF + 2 PUTs")
|
||||
if_node = build_if_node()
|
||||
put_marca = build_put_marca()
|
||||
put_suc = build_put_sucursal()
|
||||
client.add_node(wf, if_node)
|
||||
client.add_node(wf, put_marca)
|
||||
client.add_node(wf, put_suc)
|
||||
print(f" IF : {NAME_IF}")
|
||||
print(f" PUT Marca: {NAME_PUT_MARCA}")
|
||||
print(f" PUT Suc : {NAME_PUT_SUC}")
|
||||
|
||||
print(f"\n[4/6] Reconectar grafo")
|
||||
conns = wf["connections"]
|
||||
# SRC_TERMINAL -> IF
|
||||
conns[SRC_TERMINAL] = {"main": [[{"node": NAME_IF, "type": "main", "index": 0}]]}
|
||||
# IF [true]=output0 -> PUT_MARCA; IF [false]=output1 -> nada (branch vacio, no None)
|
||||
conns[NAME_IF] = {
|
||||
"main": [
|
||||
[{"node": NAME_PUT_MARCA, "type": "main", "index": 0}], # true
|
||||
[], # false: sin destino
|
||||
]
|
||||
}
|
||||
# PUT_MARCA -> PUT_SUC
|
||||
conns[NAME_PUT_MARCA] = {"main": [[{"node": NAME_PUT_SUC, "type": "main", "index": 0}]]}
|
||||
# PUT_SUC -> (terminal)
|
||||
print(f" {SRC_TERMINAL} -> {NAME_IF}")
|
||||
print(f" {NAME_IF} [true] -> {NAME_PUT_MARCA}")
|
||||
print(f" {NAME_IF} [false] -> (fin)")
|
||||
print(f" {NAME_PUT_MARCA} -> {NAME_PUT_SUC}")
|
||||
print(f" {NAME_PUT_SUC} -> (fin)")
|
||||
|
||||
print(f"\n[5/6] {'APPLY' if args.apply else 'DRY-RUN'} put_workflow")
|
||||
result = client.put_workflow(WID, wf, dry_run=not args.apply)
|
||||
if not args.apply:
|
||||
print(f" dry-run dump -> {result}")
|
||||
print("\nLISTO. Revisa el JSON. Si todo bien, corre con --apply.")
|
||||
return
|
||||
|
||||
print(f"\n[6/6] Verificar y reactivar")
|
||||
new_wf = client.verify_post(
|
||||
WID,
|
||||
expected_node_names=[NAME_IF, NAME_PUT_MARCA, NAME_PUT_SUC],
|
||||
prev_version_id=wf.get("versionId"),
|
||||
)
|
||||
print(f" versionId nuevo: {new_wf.get('versionId')}")
|
||||
if not new_wf.get("active"):
|
||||
print(" workflow inactivo tras PUT; reactivando...")
|
||||
client.activate(WID)
|
||||
new_wf = client.get_workflow(WID)
|
||||
print(f" active final : {new_wf.get('active')}")
|
||||
print(f" total nodos : {len(new_wf.get('nodes') or [])}")
|
||||
print("\nOK.")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,269 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""Fase 1 — SUCURSAL → MARCA: match primario por id_contacto_sucursal.
|
||||
|
||||
Insertar entre `Datos API Cuenta objetivo - MARCA` y `Buscar Contacto Objetivo
|
||||
- MARCA (phone)`:
|
||||
1) HTTP search por CF
|
||||
2) IF que decide CF match único
|
||||
- true → Capturar ID Match (reutilizado)
|
||||
- false → Buscar Contacto Objetivo - MARCA (phone) (cascada existente)
|
||||
|
||||
Modificar los code nodes de CREATE y UPDATE para que inyecten el CF
|
||||
`contact.id_contacto_sucursal` con el id del contacto origen, garantizando
|
||||
enlace incluso si el match cayó a la cascada.
|
||||
|
||||
Uso:
|
||||
python n8n/_apply_phase1.py # dry-run
|
||||
python n8n/_apply_phase1.py --apply
|
||||
"""
|
||||
import argparse
|
||||
import copy
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import uuid
|
||||
|
||||
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
sys.path.insert(0, ROOT)
|
||||
sys.path.insert(0, os.path.join(ROOT, "scripts"))
|
||||
from n8n_workflow_lib import load_credentials, N8NClient # noqa: E402
|
||||
|
||||
WID = "x4DqZ5FtSc43tdzB"
|
||||
CF_ID_BRAND = "E6lI9ykWhqpj7Pmi7Qd3"
|
||||
|
||||
NEW_HTTP_NAME = "Buscar Contacto Objetivo - MARCA (id_contacto_sucursal)"
|
||||
NEW_IF_NAME = "¿id_contacto_sucursal match único?"
|
||||
|
||||
|
||||
def http_node_search_by_cf():
|
||||
return {
|
||||
"parameters": {
|
||||
"method": "POST",
|
||||
"url": "https://services.leadconnectorhq.com/contacts/search",
|
||||
"sendHeaders": True,
|
||||
"headerParameters": {
|
||||
"parameters": [
|
||||
{"name": "Version", "value": "2021-07-28"},
|
||||
{"name": "Authorization",
|
||||
"value": "=Bearer {{ $('Datos API Cuenta objetivo - MARCA').item.json['Token/API'] }}"},
|
||||
]
|
||||
},
|
||||
"sendBody": True,
|
||||
"specifyBody": "json",
|
||||
"jsonBody": (
|
||||
"={\n"
|
||||
" \"locationId\": \"{{ $('Datos API Cuenta objetivo - MARCA').item.json['Location ID'] }}\",\n"
|
||||
" \"pageLimit\": 5,\n"
|
||||
" \"filters\": [\n"
|
||||
" {\n"
|
||||
" \"group\": \"AND\",\n"
|
||||
" \"filters\": [\n"
|
||||
" {\n"
|
||||
" \"field\": \"customFields." + CF_ID_BRAND + "\",\n"
|
||||
" \"operator\": \"eq\",\n"
|
||||
" \"value\": \"{{ $('Crear Contacto').item.json.body.contact_id }}\"\n"
|
||||
" }\n"
|
||||
" ]\n"
|
||||
" }\n"
|
||||
" ]\n"
|
||||
"}"
|
||||
),
|
||||
"options": {"redirect": {"redirect": {}}},
|
||||
},
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"typeVersion": 4.2,
|
||||
"position": [2832, -496],
|
||||
"id": str(uuid.uuid4()),
|
||||
"name": NEW_HTTP_NAME,
|
||||
"onError": "continueRegularOutput",
|
||||
"alwaysOutputData": True,
|
||||
"notes": (
|
||||
"FASE 1 — Match primario por contact.id_contacto_sucursal. "
|
||||
"Si total==1, el siguiente IF deriva a Capturar ID Match. "
|
||||
"Si no, sigue a la cascada phone→email→nombre."
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
def if_node_cf_match():
|
||||
return {
|
||||
"parameters": {
|
||||
"conditions": {
|
||||
"options": {
|
||||
"caseSensitive": True,
|
||||
"leftValue": "",
|
||||
"typeValidation": "strict",
|
||||
"version": 3,
|
||||
},
|
||||
"conditions": [
|
||||
{
|
||||
"id": str(uuid.uuid4()),
|
||||
"leftValue": "={{ $json.total }}",
|
||||
"rightValue": 1,
|
||||
"operator": {"type": "number", "operation": "equals"},
|
||||
}
|
||||
],
|
||||
"combinator": "and",
|
||||
},
|
||||
"options": {},
|
||||
},
|
||||
"type": "n8n-nodes-base.if",
|
||||
"typeVersion": 2,
|
||||
"position": [3040, -496],
|
||||
"id": str(uuid.uuid4()),
|
||||
"name": NEW_IF_NAME,
|
||||
"notes": "FASE 1. true → Capturar ID Match. false → cascada phone.",
|
||||
}
|
||||
|
||||
|
||||
# ---- Patch helpers para code nodes ----
|
||||
|
||||
INJECT_BLOCK_UPDATE = (
|
||||
"\n// === FASE 1: inyectar CF id_contacto_sucursal (defensive) ===\n"
|
||||
"var __linkId = '" + CF_ID_BRAND + "';\n"
|
||||
"customFields = customFields.filter(function(c){ return c.id !== __linkId; });\n"
|
||||
"if (origenData && origenData.id) {\n"
|
||||
" customFields.push({ id: __linkId, key: 'contact.id_contacto_sucursal', field_value: origenData.id });\n"
|
||||
"}\n"
|
||||
)
|
||||
|
||||
INJECT_BLOCK_CREATE_FK = (
|
||||
"\n// === FASE 1: garantizar CF contact.id_contacto_sucursal ===\n"
|
||||
"(function(){\n"
|
||||
" var origenId = ($('Obtener Contacto Cuenta Origen - SUCURSAL').first().json.contact || {}).id;\n"
|
||||
" if (!origenId) return;\n"
|
||||
" // Encontrar el def en Marca por fieldKey.\n"
|
||||
" var defLink = marcaCustomDefs.find(function(d){ return d.fieldKey === 'contact.id_contacto_sucursal'; });\n"
|
||||
" if (!defLink) return;\n"
|
||||
" customFields = customFields.filter(function(c){ return c.id !== defLink.id; });\n"
|
||||
" customFields.push({ id: defLink.id, field_value: origenId });\n"
|
||||
"})();\n"
|
||||
)
|
||||
|
||||
|
||||
def patch_update_code(wf, client):
|
||||
"""Modifica `Obtener Body para Actualizar Contacto Objetivo - MARCA`."""
|
||||
node = client.find_node(wf, "Obtener Body para Actualizar Contacto Objetivo - MARCA")
|
||||
if not node:
|
||||
raise RuntimeError("nodo update no encontrado")
|
||||
code = node["parameters"].get("jsCode") or ""
|
||||
if "FASE 1: inyectar CF id_contacto_sucursal" in code:
|
||||
print(" [skip] code update ya tiene el bloque FASE 1")
|
||||
return False
|
||||
# Insertar antes de `var body = {`
|
||||
marker = "var body = {"
|
||||
idx = code.rfind(marker)
|
||||
if idx < 0:
|
||||
raise RuntimeError("marker 'var body = {' no encontrado en code update")
|
||||
new_code = code[:idx] + INJECT_BLOCK_UPDATE + code[idx:]
|
||||
node["parameters"]["jsCode"] = new_code
|
||||
return True
|
||||
|
||||
|
||||
def patch_create_code(wf, client):
|
||||
"""Modifica `Obtener el body para crear Contacto - MARCA`."""
|
||||
node = client.find_node(wf, "Obtener el body para crear Contacto - MARCA")
|
||||
if not node:
|
||||
raise RuntimeError("nodo create no encontrado")
|
||||
code = node["parameters"].get("jsCode") or ""
|
||||
if "FASE 1: garantizar CF contact.id_contacto_sucursal" in code:
|
||||
print(" [skip] code create ya tiene el bloque FASE 1")
|
||||
return False
|
||||
marker = "var body = {"
|
||||
idx = code.rfind(marker)
|
||||
if idx < 0:
|
||||
raise RuntimeError("marker 'var body = {' no encontrado en code create")
|
||||
new_code = code[:idx] + INJECT_BLOCK_CREATE_FK + code[idx:]
|
||||
node["parameters"]["jsCode"] = new_code
|
||||
return True
|
||||
|
||||
|
||||
# ---- Main ----
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("--apply", action="store_true", help="Aplica al workflow. Sin esto: dry-run.")
|
||||
parser.add_argument("--activate", action="store_true", help="Tras apply OK, activar el workflow.")
|
||||
args = parser.parse_args()
|
||||
if hasattr(sys.stdout, "reconfigure"):
|
||||
sys.stdout.reconfigure(encoding="utf-8")
|
||||
|
||||
client = N8NClient(*load_credentials())
|
||||
print(f"[fase 1] GET workflow {WID}")
|
||||
wf, backup_path = client.backup_workflow(WID, label="fase1_pre")
|
||||
prev_version = wf.get("versionId")
|
||||
print(f" backup: {backup_path}")
|
||||
print(f" versionId: {prev_version} active: {wf.get('active')}")
|
||||
print(f" nodes pre: {len(wf['nodes'])}")
|
||||
|
||||
# Trabajar sobre copia
|
||||
wfm = copy.deepcopy(wf)
|
||||
|
||||
# Idempotencia
|
||||
try:
|
||||
client.assert_idempotent(wfm, NEW_HTTP_NAME)
|
||||
client.assert_idempotent(wfm, NEW_IF_NAME)
|
||||
except Exception as e:
|
||||
raise SystemExit(f"Aborto: {e}")
|
||||
|
||||
# Insertar nodos
|
||||
print("[fase 1] insertando nodos nuevos...")
|
||||
http_node = http_node_search_by_cf()
|
||||
if_node = if_node_cf_match()
|
||||
client.add_node(wfm, http_node)
|
||||
client.add_node(wfm, if_node)
|
||||
|
||||
# Reconectar:
|
||||
# antes: Datos API Cuenta objetivo - MARCA → Buscar Contacto Objetivo - MARCA (phone)
|
||||
# ahora: Datos API → HTTP nuevo → IF nuevo
|
||||
# IF true → Capturar ID Match
|
||||
# IF false → Buscar Contacto Objetivo - MARCA (phone)
|
||||
client.set_connection(wfm, "Datos API Cuenta objetivo - MARCA", NEW_HTTP_NAME)
|
||||
client.set_connection(wfm, NEW_HTTP_NAME, NEW_IF_NAME)
|
||||
client.branch_if(wfm, NEW_IF_NAME,
|
||||
true_target="Capturar ID Match",
|
||||
false_target="Buscar Contacto Objetivo - MARCA (phone)")
|
||||
|
||||
# Modificar code nodes
|
||||
print("[fase 1] modificando code UPDATE...")
|
||||
patch_update_code(wfm, client)
|
||||
print("[fase 1] modificando code CREATE...")
|
||||
patch_create_code(wfm, client)
|
||||
|
||||
print(f"[fase 1] nodes post: {len(wfm['nodes'])} (+{len(wfm['nodes'])-len(wf['nodes'])})")
|
||||
|
||||
# Dry-run
|
||||
print("[fase 1] dry-run PUT (dump a archivo)...")
|
||||
res = client.put_workflow(WID, wfm, dry_run=True)
|
||||
print(f" dry-run path: {res['path']}")
|
||||
|
||||
if not args.apply:
|
||||
print("\nDRY-RUN. Para aplicar: --apply [--activate]")
|
||||
return
|
||||
|
||||
# Apply real
|
||||
print("[fase 1] PUT real...")
|
||||
client.put_workflow(WID, wfm, dry_run=False)
|
||||
print(" PUT OK. Verificando...")
|
||||
wf_post = client.verify_post(
|
||||
WID,
|
||||
expected_node_names=[NEW_HTTP_NAME, NEW_IF_NAME],
|
||||
prev_version_id=prev_version,
|
||||
)
|
||||
print(f" versionId nuevo: {wf_post.get('versionId')}")
|
||||
print(f" active post-PUT: {wf_post.get('active')}")
|
||||
|
||||
if args.activate or not wf_post.get("active"):
|
||||
print("[fase 1] activando workflow...")
|
||||
client.activate(WID)
|
||||
wf_post2 = client.get_workflow(WID)
|
||||
print(f" active final: {wf_post2.get('active')}")
|
||||
|
||||
print("\n[fase 1] aplicado y activado.")
|
||||
print("Siguiente paso: python scripts/n8n_e2e_test.py --scenario all-phase1")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,84 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""Fase 1 FIX: el nodo `Buscar Contacto Objetivo - MARCA (phone)` referenciaba
|
||||
`$json['Token/API']` y `$json['Location ID']` asumiendo que el input previo era
|
||||
`Datos API Cuenta objetivo - MARCA`. Tras la Fase 1, el input proviene del IF
|
||||
nuevo (que solo trae `contacts` y `total`). Cambiamos las refs a explícitas.
|
||||
|
||||
Idempotente.
|
||||
"""
|
||||
import argparse
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
|
||||
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
sys.path.insert(0, ROOT)
|
||||
sys.path.insert(0, os.path.join(ROOT, "scripts"))
|
||||
from n8n_workflow_lib import load_credentials, N8NClient # noqa: E402
|
||||
|
||||
WID = "x4DqZ5FtSc43tdzB"
|
||||
TARGET_NODE = "Buscar Contacto Objetivo - MARCA (phone)"
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("--apply", action="store_true")
|
||||
args = parser.parse_args()
|
||||
if hasattr(sys.stdout, "reconfigure"):
|
||||
sys.stdout.reconfigure(encoding="utf-8")
|
||||
|
||||
client = N8NClient(*load_credentials())
|
||||
wf, backup_path = client.backup_workflow(WID, label="fase1_fix_phone_refs")
|
||||
print(f"backup: {backup_path}")
|
||||
prev = wf.get("versionId")
|
||||
|
||||
node = client.find_node(wf, TARGET_NODE)
|
||||
if not node:
|
||||
raise SystemExit(f"nodo {TARGET_NODE!r} no encontrado")
|
||||
|
||||
p = node["parameters"]
|
||||
# Cambiar Authorization header
|
||||
for h in p.get("headerParameters", {}).get("parameters", []):
|
||||
if h.get("name") == "Authorization":
|
||||
old = h["value"]
|
||||
new = old.replace(
|
||||
"$json['Token/API']",
|
||||
"$('Datos API Cuenta objetivo - MARCA').item.json['Token/API']"
|
||||
)
|
||||
if old != new:
|
||||
h["value"] = new
|
||||
print(f" Authorization: actualizado")
|
||||
else:
|
||||
print(f" Authorization: ya tenía referencia explícita (skip)")
|
||||
|
||||
# Cambiar jsonBody locationId
|
||||
body = p.get("jsonBody") or ""
|
||||
new_body = body.replace(
|
||||
"$json['Location ID']",
|
||||
"$('Datos API Cuenta objetivo - MARCA').item.json['Location ID']"
|
||||
)
|
||||
if new_body != body:
|
||||
p["jsonBody"] = new_body
|
||||
print(f" jsonBody locationId: actualizado")
|
||||
else:
|
||||
print(f" jsonBody locationId: ya tenía referencia explícita (skip)")
|
||||
|
||||
# Dry-run / apply
|
||||
if not args.apply:
|
||||
res = client.put_workflow(WID, wf, dry_run=True)
|
||||
print(f"DRY-RUN. path: {res['path']}")
|
||||
return
|
||||
|
||||
client.put_workflow(WID, wf, dry_run=False)
|
||||
wf2 = client.verify_post(WID, expected_node_names=[TARGET_NODE], prev_version_id=prev)
|
||||
print(f"versionId nuevo: {wf2.get('versionId')}")
|
||||
print(f"active: {wf2.get('active')}")
|
||||
if not wf2.get("active"):
|
||||
client.activate(WID)
|
||||
print("activado.")
|
||||
print("OK")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,217 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""Fase 2 — MARCA → SUCURSAL V2: match directo por id_contacto_sucursal.
|
||||
|
||||
Insertar entre `Datos API Cuenta objetivo - SUCURSAL` y
|
||||
`Buscar Contacto Objetivo - SUCURSAL(mail)`:
|
||||
1) Code `Extraer id_contacto_sucursal de Marca` — saca CF del contacto Marca.
|
||||
2) IF `¿cfValue presente?` — si vacío salta directo a cascada email.
|
||||
3) HTTP GET `Match directo por id_contacto_sucursal` — GET /contacts/{cfValue}
|
||||
con token sucursal. onError: continueRegularOutput.
|
||||
4) IF `¿GET de contacto sucursal OK?` — true→Conseguir Custom...SUCURSAL (UPDATE),
|
||||
false→cascada email.
|
||||
|
||||
Uso:
|
||||
python n8n/_apply_phase2.py # dry-run
|
||||
python n8n/_apply_phase2.py --apply [--activate]
|
||||
"""
|
||||
import argparse
|
||||
import copy
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
import uuid
|
||||
|
||||
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
sys.path.insert(0, ROOT)
|
||||
sys.path.insert(0, os.path.join(ROOT, "scripts"))
|
||||
from n8n_workflow_lib import load_credentials, N8NClient # noqa: E402
|
||||
|
||||
WID = "4UMRwxJdHFfOGHBp"
|
||||
CF_ID_BRAND = "E6lI9ykWhqpj7Pmi7Qd3"
|
||||
|
||||
CODE_EXTRACT_NAME = "Extraer id_contacto_sucursal de Marca"
|
||||
IF_CF_PRESENT_NAME = "¿cfValue presente?"
|
||||
HTTP_GET_NAME = "Match directo por id_contacto_sucursal"
|
||||
IF_GET_OK_NAME = "¿GET de contacto sucursal OK?"
|
||||
|
||||
|
||||
def code_extract_node():
|
||||
return {
|
||||
"parameters": {
|
||||
"jsCode": (
|
||||
"var c = $('Obtener Contacto Cuenta Origen - SUCURSAL').first().json.contact || {};\n"
|
||||
"var cfs = c.customFields || [];\n"
|
||||
"var v = null;\n"
|
||||
"for (var i = 0; i < cfs.length; i++) {\n"
|
||||
" if (cfs[i].id === '" + CF_ID_BRAND + "') { v = cfs[i].value; break; }\n"
|
||||
"}\n"
|
||||
"return [{ json: { cfValue: v || null } }];"
|
||||
)
|
||||
},
|
||||
"type": "n8n-nodes-base.code",
|
||||
"typeVersion": 2,
|
||||
"position": [1376, 416],
|
||||
"id": str(uuid.uuid4()),
|
||||
"name": CODE_EXTRACT_NAME,
|
||||
"notes": "FASE 2. Extrae el valor del CF id_contacto_sucursal del contacto Marca origen.",
|
||||
}
|
||||
|
||||
|
||||
def if_cf_present_node():
|
||||
return {
|
||||
"parameters": {
|
||||
"conditions": {
|
||||
"options": {
|
||||
"caseSensitive": True,
|
||||
"leftValue": "",
|
||||
"typeValidation": "loose",
|
||||
"version": 3,
|
||||
},
|
||||
"conditions": [
|
||||
{
|
||||
"id": str(uuid.uuid4()),
|
||||
"leftValue": "={{ $json.cfValue }}",
|
||||
"rightValue": "",
|
||||
"operator": {"type": "string", "operation": "notEmpty", "singleValue": True},
|
||||
}
|
||||
],
|
||||
"combinator": "and",
|
||||
},
|
||||
"options": {},
|
||||
},
|
||||
"type": "n8n-nodes-base.if",
|
||||
"typeVersion": 2,
|
||||
"position": [1568, 416],
|
||||
"id": str(uuid.uuid4()),
|
||||
"name": IF_CF_PRESENT_NAME,
|
||||
"notes": "true→HTTP GET, false→cascada email",
|
||||
}
|
||||
|
||||
|
||||
def http_get_node():
|
||||
return {
|
||||
"parameters": {
|
||||
"method": "GET",
|
||||
"url": "=https://services.leadconnectorhq.com/contacts/{{ $('" + CODE_EXTRACT_NAME + "').item.json.cfValue }}",
|
||||
"sendHeaders": True,
|
||||
"headerParameters": {
|
||||
"parameters": [
|
||||
{"name": "Version", "value": "2021-07-28"},
|
||||
{"name": "Authorization",
|
||||
"value": "=Bearer {{ $('Datos API Cuenta objetivo - SUCURSAL').item.json['Token/API'] }}"},
|
||||
]
|
||||
},
|
||||
"options": {"redirect": {"redirect": {}}},
|
||||
},
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"typeVersion": 4.2,
|
||||
"position": [1760, 416],
|
||||
"id": str(uuid.uuid4()),
|
||||
"name": HTTP_GET_NAME,
|
||||
"onError": "continueRegularOutput",
|
||||
"alwaysOutputData": True,
|
||||
"notes": "GET directo a sucursal por id (el valor del CF). Si 404, IF siguiente cae a cascada.",
|
||||
}
|
||||
|
||||
|
||||
def if_get_ok_node():
|
||||
return {
|
||||
"parameters": {
|
||||
"conditions": {
|
||||
"options": {
|
||||
"caseSensitive": True,
|
||||
"leftValue": "",
|
||||
"typeValidation": "loose",
|
||||
"version": 3,
|
||||
},
|
||||
"conditions": [
|
||||
{
|
||||
"id": str(uuid.uuid4()),
|
||||
"leftValue": "={{ $json.contact && $json.contact.id ? 'ok' : '' }}",
|
||||
"rightValue": "",
|
||||
"operator": {"type": "string", "operation": "notEmpty", "singleValue": True},
|
||||
}
|
||||
],
|
||||
"combinator": "and",
|
||||
},
|
||||
"options": {},
|
||||
},
|
||||
"type": "n8n-nodes-base.if",
|
||||
"typeVersion": 2,
|
||||
"position": [1952, 416],
|
||||
"id": str(uuid.uuid4()),
|
||||
"name": IF_GET_OK_NAME,
|
||||
"notes": "true→UPDATE (Conseguir Custom Cuenta objetivo - SUCURSAL), false→cascada email",
|
||||
}
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("--apply", action="store_true")
|
||||
parser.add_argument("--activate", action="store_true")
|
||||
args = parser.parse_args()
|
||||
if hasattr(sys.stdout, "reconfigure"):
|
||||
sys.stdout.reconfigure(encoding="utf-8")
|
||||
|
||||
client = N8NClient(*load_credentials())
|
||||
wf, backup = client.backup_workflow(WID, label="fase2_pre")
|
||||
print(f"backup: {backup}")
|
||||
prev = wf.get("versionId")
|
||||
print(f"versionId pre: {prev} active: {wf.get('active')} nodes: {len(wf['nodes'])}")
|
||||
|
||||
wfm = copy.deepcopy(wf)
|
||||
for name in (CODE_EXTRACT_NAME, IF_CF_PRESENT_NAME, HTTP_GET_NAME, IF_GET_OK_NAME):
|
||||
try:
|
||||
client.assert_idempotent(wfm, name)
|
||||
except Exception as e:
|
||||
raise SystemExit(f"Aborto: {e}")
|
||||
|
||||
# Insertar nodos
|
||||
code_node = code_extract_node()
|
||||
if_cf = if_cf_present_node()
|
||||
http_get = http_get_node()
|
||||
if_ok = if_get_ok_node()
|
||||
for n in (code_node, if_cf, http_get, if_ok):
|
||||
client.add_node(wfm, n)
|
||||
|
||||
# Conexiones:
|
||||
# Datos API Cuenta objetivo - SUCURSAL → Code Extract
|
||||
# Code Extract → IF cfValue presente
|
||||
# true → HTTP GET
|
||||
# → IF GET OK
|
||||
# true → Conseguir Custom Cuenta objetivo - SUCURSAL
|
||||
# false → Buscar Contacto Objetivo - SUCURSAL(mail)
|
||||
# false → Buscar Contacto Objetivo - SUCURSAL(mail)
|
||||
client.set_connection(wfm, "Datos API Cuenta objetivo - SUCURSAL", CODE_EXTRACT_NAME)
|
||||
client.set_connection(wfm, CODE_EXTRACT_NAME, IF_CF_PRESENT_NAME)
|
||||
client.branch_if(wfm, IF_CF_PRESENT_NAME,
|
||||
true_target=HTTP_GET_NAME,
|
||||
false_target="Buscar Contacto Objetivo - SUCURSAL(mail)")
|
||||
client.set_connection(wfm, HTTP_GET_NAME, IF_GET_OK_NAME)
|
||||
client.branch_if(wfm, IF_GET_OK_NAME,
|
||||
true_target="Conseguir Custom Cuenta objetivo - SUCURSAL",
|
||||
false_target="Buscar Contacto Objetivo - SUCURSAL(mail)")
|
||||
|
||||
print(f"nodes post: {len(wfm['nodes'])} (+{len(wfm['nodes'])-len(wf['nodes'])})")
|
||||
|
||||
if not args.apply:
|
||||
res = client.put_workflow(WID, wfm, dry_run=True)
|
||||
print(f"DRY-RUN. {res['path']}")
|
||||
return
|
||||
|
||||
client.put_workflow(WID, wfm, dry_run=False)
|
||||
wf2 = client.verify_post(WID,
|
||||
expected_node_names=[CODE_EXTRACT_NAME, IF_CF_PRESENT_NAME,
|
||||
HTTP_GET_NAME, IF_GET_OK_NAME],
|
||||
prev_version_id=prev)
|
||||
print(f"versionId nuevo: {wf2.get('versionId')} active: {wf2.get('active')}")
|
||||
if args.activate or not wf2.get("active"):
|
||||
client.activate(WID)
|
||||
print("activado")
|
||||
|
||||
print("Fase 2 aplicada.")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,61 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""Fix post-Fase 2/4: el code `Obtener datos completos de Contacto objetivo -
|
||||
SUCURSAL` referencia a `Obtener Contacto Cuenta objetivo - SUCURSAL` directamente,
|
||||
pero en la rama de match por CF ese nodo no se ejecuta (el contacto viene de
|
||||
`Match directo por id_contacto_sucursal`). Hacemos el code defensivo: probamos
|
||||
ambos nodos y usamos el que tenga datos.
|
||||
|
||||
Idempotente.
|
||||
"""
|
||||
import argparse, os, sys
|
||||
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
sys.path.insert(0, ROOT); sys.path.insert(0, os.path.join(ROOT, "scripts"))
|
||||
from n8n_workflow_lib import load_credentials, N8NClient # noqa: E402
|
||||
|
||||
WID = "4UMRwxJdHFfOGHBp"
|
||||
TARGET = "Obtener datos completos de Contacto objetivo - SUCURSAL"
|
||||
MARKER = "// FIX FASE 2: input from match by CF or cascade"
|
||||
|
||||
NEW_PREAMBLE = (
|
||||
MARKER + "\n"
|
||||
"var contactData;\n"
|
||||
"try {\n"
|
||||
" contactData = $('Match directo por id_contacto_sucursal').first().json.contact;\n"
|
||||
"} catch (e) { contactData = null; }\n"
|
||||
"if (!contactData || !contactData.id) {\n"
|
||||
" contactData = $('Obtener Contacto Cuenta objetivo - SUCURSAL').first().json.contact;\n"
|
||||
"}\n"
|
||||
)
|
||||
|
||||
|
||||
def main():
|
||||
p = argparse.ArgumentParser()
|
||||
p.add_argument("--apply", action="store_true")
|
||||
args = p.parse_args()
|
||||
if hasattr(sys.stdout, "reconfigure"): sys.stdout.reconfigure(encoding="utf-8")
|
||||
c = N8NClient(*load_credentials())
|
||||
wf, b = c.backup_workflow(WID, label="fase2_fix_code_ref")
|
||||
print(f"backup: {b}")
|
||||
prev = wf.get("versionId")
|
||||
n = c.find_node(wf, TARGET)
|
||||
if not n: raise SystemExit(f"nodo {TARGET!r} no encontrado")
|
||||
code = n["parameters"].get("jsCode") or ""
|
||||
if MARKER in code:
|
||||
print("ya parchado (skip)"); return
|
||||
old_line = "const contactData = $('Obtener Contacto Cuenta objetivo - SUCURSAL').first().json.contact;"
|
||||
if old_line not in code:
|
||||
print("línea esperada no encontrada; revisa manualmente"); print(code[:400]); return
|
||||
new_code = code.replace(old_line, NEW_PREAMBLE)
|
||||
n["parameters"]["jsCode"] = new_code
|
||||
if not args.apply:
|
||||
res = c.put_workflow(WID, wf, dry_run=True); print(f"DRY-RUN: {res['path']}"); return
|
||||
c.put_workflow(WID, wf, dry_run=False)
|
||||
wf2 = c.verify_post(WID, expected_node_names=[TARGET], prev_version_id=prev)
|
||||
print(f"versionId nuevo: {wf2.get('versionId')} active: {wf2.get('active')}")
|
||||
if not wf2.get("active"): c.activate(WID); print("activado")
|
||||
print("OK")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,184 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""Fase 3 — MARCA → SUCURSAL V2: autoenlace bidireccional post-CREATE.
|
||||
|
||||
Tras `Crear Contacto - Cuenta Objetivo - MARCA` (nombre engañoso, crea en sucursal):
|
||||
1) Code `Resolver fieldId y newContactId post-CREATE` — extrae el fieldId del CF
|
||||
en SUCURSAL desde `Conseguir Custom Cuenta objetivo - MARCA1` y el id del
|
||||
contacto recién creado.
|
||||
2) HTTP PUT `Enlazar CF en SUCURSAL (autoref)` — pone CF=newContactId en sucursal.
|
||||
3) HTTP PUT `Enlazar CF en MARCA` — pone CF=newContactId en el contacto Marca origen.
|
||||
|
||||
Después seguimos al `Envio a tienda` original.
|
||||
"""
|
||||
import argparse
|
||||
import copy
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
import uuid
|
||||
|
||||
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
sys.path.insert(0, ROOT)
|
||||
sys.path.insert(0, os.path.join(ROOT, "scripts"))
|
||||
from n8n_workflow_lib import load_credentials, N8NClient # noqa: E402
|
||||
|
||||
WID = "4UMRwxJdHFfOGHBp"
|
||||
CF_ID_BRAND = "E6lI9ykWhqpj7Pmi7Qd3"
|
||||
|
||||
CODE_RESOLVE = "Resolver fieldId y newContactId post-CREATE"
|
||||
PUT_SUC = "Enlazar CF en SUCURSAL (autoref)"
|
||||
PUT_MARCA = "Enlazar CF en MARCA"
|
||||
|
||||
|
||||
def code_resolve_node():
|
||||
return {
|
||||
"parameters": {
|
||||
"jsCode": (
|
||||
"var defs = $('Conseguir Custom Cuenta objetivo - MARCA1').first().json.customFields || [];\n"
|
||||
"var def = defs.find(function(d){ return d.fieldKey === 'contact.id_contacto_sucursal'; });\n"
|
||||
"var createdContact = ($('Crear Contacto - Cuenta Objetivo - MARCA').first().json || {}).contact || {};\n"
|
||||
"var newId = createdContact.id;\n"
|
||||
"return [{ json: { fieldId: def ? def.id : null, newContactId: newId || null } }];"
|
||||
)
|
||||
},
|
||||
"type": "n8n-nodes-base.code",
|
||||
"typeVersion": 2,
|
||||
"position": [3920, 848],
|
||||
"id": str(uuid.uuid4()),
|
||||
"name": CODE_RESOLVE,
|
||||
"notes": "FASE 3. Extrae fieldId del CF en sucursal y el id del contacto recién creado.",
|
||||
}
|
||||
|
||||
|
||||
def put_sucursal_node():
|
||||
return {
|
||||
"parameters": {
|
||||
"method": "PUT",
|
||||
"url": "=https://services.leadconnectorhq.com/contacts/{{ $json.newContactId }}",
|
||||
"sendHeaders": True,
|
||||
"headerParameters": {
|
||||
"parameters": [
|
||||
{"name": "Accept", "value": "application/json"},
|
||||
{"name": "Version", "value": "2021-07-28"},
|
||||
{"name": "Authorization",
|
||||
"value": "=Bearer {{ $('Datos API Cuenta objetivo - SUCURSAL').item.json['Token/API'] }}"},
|
||||
{"name": "Content-Type", "value": "application/json"},
|
||||
]
|
||||
},
|
||||
"sendBody": True,
|
||||
"specifyBody": "json",
|
||||
"jsonBody": (
|
||||
"={\n"
|
||||
" \"customFields\": [\n"
|
||||
" {\n"
|
||||
" \"id\": \"{{ $json.fieldId }}\",\n"
|
||||
" \"key\": \"contact.id_contacto_sucursal\",\n"
|
||||
" \"field_value\": \"{{ $json.newContactId }}\"\n"
|
||||
" }\n"
|
||||
" ]\n"
|
||||
"}"
|
||||
),
|
||||
"options": {"redirect": {"redirect": {}}},
|
||||
},
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"typeVersion": 4.2,
|
||||
"position": [4112, 848],
|
||||
"id": str(uuid.uuid4()),
|
||||
"name": PUT_SUC,
|
||||
"onError": "continueRegularOutput",
|
||||
"notes": "FASE 3. PUT al contacto sucursal recién creado: CF id_contacto_sucursal = su propio id.",
|
||||
}
|
||||
|
||||
|
||||
def put_marca_node():
|
||||
return {
|
||||
"parameters": {
|
||||
"method": "PUT",
|
||||
"url": "=https://services.leadconnectorhq.com/contacts/{{ $('Webhook').item.json.body.contact_id }}",
|
||||
"sendHeaders": True,
|
||||
"headerParameters": {
|
||||
"parameters": [
|
||||
{"name": "Accept", "value": "application/json"},
|
||||
{"name": "Version", "value": "2021-07-28"},
|
||||
{"name": "Authorization",
|
||||
"value": "=Bearer {{ $('Datos API Cuenta Origen').item.json['Token/API'] }}"},
|
||||
{"name": "Content-Type", "value": "application/json"},
|
||||
]
|
||||
},
|
||||
"sendBody": True,
|
||||
"specifyBody": "json",
|
||||
"jsonBody": (
|
||||
"={\n"
|
||||
" \"customFields\": [\n"
|
||||
" {\n"
|
||||
" \"id\": \"" + CF_ID_BRAND + "\",\n"
|
||||
" \"key\": \"contact.id_contacto_sucursal\",\n"
|
||||
" \"field_value\": \"{{ $('" + CODE_RESOLVE + "').item.json.newContactId }}\"\n"
|
||||
" }\n"
|
||||
" ]\n"
|
||||
"}"
|
||||
),
|
||||
"options": {"redirect": {"redirect": {}}},
|
||||
},
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"typeVersion": 4.2,
|
||||
"position": [4304, 848],
|
||||
"id": str(uuid.uuid4()),
|
||||
"name": PUT_MARCA,
|
||||
"onError": "continueRegularOutput",
|
||||
"notes": "FASE 3. PUT al contacto Marca origen: CF id_contacto_sucursal = id del nuevo sucursal.",
|
||||
}
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("--apply", action="store_true")
|
||||
parser.add_argument("--activate", action="store_true")
|
||||
args = parser.parse_args()
|
||||
if hasattr(sys.stdout, "reconfigure"):
|
||||
sys.stdout.reconfigure(encoding="utf-8")
|
||||
|
||||
client = N8NClient(*load_credentials())
|
||||
wf, backup = client.backup_workflow(WID, label="fase3_pre")
|
||||
print(f"backup: {backup}")
|
||||
prev = wf.get("versionId")
|
||||
print(f"versionId pre: {prev} active: {wf.get('active')}")
|
||||
|
||||
wfm = copy.deepcopy(wf)
|
||||
for name in (CODE_RESOLVE, PUT_SUC, PUT_MARCA):
|
||||
client.assert_idempotent(wfm, name)
|
||||
|
||||
code_node = code_resolve_node()
|
||||
put_suc = put_sucursal_node()
|
||||
put_marca = put_marca_node()
|
||||
for n in (code_node, put_suc, put_marca):
|
||||
client.add_node(wfm, n)
|
||||
|
||||
# Conexiones: Crear → Code → PUT suc → PUT Marca → Envio a tienda
|
||||
# antes: Crear → Envio a tienda
|
||||
client.set_connection(wfm, "Crear Contacto - Cuenta Objetivo - MARCA", CODE_RESOLVE)
|
||||
client.set_connection(wfm, CODE_RESOLVE, PUT_SUC)
|
||||
client.set_connection(wfm, PUT_SUC, PUT_MARCA)
|
||||
client.set_connection(wfm, PUT_MARCA, "Envio a tienda")
|
||||
|
||||
print(f"nodes post: {len(wfm['nodes'])} (+{len(wfm['nodes'])-len(wf['nodes'])})")
|
||||
|
||||
if not args.apply:
|
||||
res = client.put_workflow(WID, wfm, dry_run=True)
|
||||
print(f"DRY-RUN. {res['path']}")
|
||||
return
|
||||
|
||||
client.put_workflow(WID, wfm, dry_run=False)
|
||||
wf2 = client.verify_post(WID, expected_node_names=[CODE_RESOLVE, PUT_SUC, PUT_MARCA],
|
||||
prev_version_id=prev)
|
||||
print(f"versionId nuevo: {wf2.get('versionId')} active: {wf2.get('active')}")
|
||||
if args.activate or not wf2.get("active"):
|
||||
client.activate(WID)
|
||||
print("activado")
|
||||
|
||||
print("Fase 3 aplicada.")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,89 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""Fase 4 — Renombres cosméticos del workflow Marca→Sucursal V2.
|
||||
|
||||
Los nombres con sufijo "MARCA" o "SUCURSAL" en ese workflow son inconsistentes
|
||||
porque el JSON fue copy-pasted del workflow inverso. Esta fase renombra usando
|
||||
`rename_node` que actualiza también todas las referencias `$('OLD')` en
|
||||
parameters y `connections`.
|
||||
|
||||
Idempotente: si ya están renombrados, skip por assert_idempotent inverso.
|
||||
"""
|
||||
import argparse
|
||||
import copy
|
||||
import os
|
||||
import sys
|
||||
|
||||
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
sys.path.insert(0, ROOT)
|
||||
sys.path.insert(0, os.path.join(ROOT, "scripts"))
|
||||
from n8n_workflow_lib import load_credentials, N8NClient, AlreadyExistsError # noqa: E402
|
||||
|
||||
WID = "4UMRwxJdHFfOGHBp"
|
||||
|
||||
RENAMES = [
|
||||
# (nombre actual, nombre nuevo)
|
||||
("Crear Contacto - Cuenta Objetivo - MARCA", "Crear Contacto - Cuenta Objetivo - SUCURSAL"),
|
||||
("Conseguir Custom Cuenta objetivo - MARCA1", "Conseguir Custom Cuenta objetivo - SUCURSAL (CREATE)"),
|
||||
("Obtener Contacto Cuenta Origen - SUCURSAL", "Obtener Contacto Cuenta Origen - MARCA"),
|
||||
("Conseguir Custom Cuenta Origen- SUCURSAL", "Conseguir Custom Cuenta Origen - MARCA"),
|
||||
("Obtener datos completos de Contacto origen - SUCURSAL", "Obtener datos completos de Contacto origen - MARCA"),
|
||||
("Obtener el body para crear Contacto - MARCA", "Obtener el body para crear Contacto - SUCURSAL"),
|
||||
]
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("--apply", action="store_true")
|
||||
parser.add_argument("--activate", action="store_true")
|
||||
args = parser.parse_args()
|
||||
if hasattr(sys.stdout, "reconfigure"):
|
||||
sys.stdout.reconfigure(encoding="utf-8")
|
||||
|
||||
client = N8NClient(*load_credentials())
|
||||
wf, backup = client.backup_workflow(WID, label="fase4_pre")
|
||||
print(f"backup: {backup}")
|
||||
prev = wf.get("versionId")
|
||||
print(f"versionId pre: {prev} active: {wf.get('active')}")
|
||||
|
||||
wfm = copy.deepcopy(wf)
|
||||
applied = 0
|
||||
expected_post = []
|
||||
for old, new in RENAMES:
|
||||
# Si el nodo viejo no existe, asumimos que ya está renombrado (idempotente)
|
||||
if client.find_node(wfm, old) is None:
|
||||
if client.find_node(wfm, new) is not None:
|
||||
print(f" [skip] {old!r} → {new!r} (ya renombrado)")
|
||||
expected_post.append(new)
|
||||
continue
|
||||
else:
|
||||
raise SystemExit(f"FAIL: {old!r} no existe y {new!r} tampoco.")
|
||||
try:
|
||||
client.rename_node(wfm, old, new)
|
||||
print(f" [rename] {old!r} → {new!r}")
|
||||
applied += 1
|
||||
expected_post.append(new)
|
||||
except AlreadyExistsError as e:
|
||||
raise SystemExit(f"FAIL: {e}")
|
||||
|
||||
if applied == 0:
|
||||
print("Nada que renombrar.")
|
||||
return
|
||||
|
||||
if not args.apply:
|
||||
res = client.put_workflow(WID, wfm, dry_run=True)
|
||||
print(f"DRY-RUN. {res['path']}")
|
||||
return
|
||||
|
||||
client.put_workflow(WID, wfm, dry_run=False)
|
||||
wf2 = client.verify_post(WID, expected_node_names=expected_post, prev_version_id=prev)
|
||||
print(f"versionId nuevo: {wf2.get('versionId')} active: {wf2.get('active')}")
|
||||
if args.activate or not wf2.get("active"):
|
||||
client.activate(WID)
|
||||
print("activado")
|
||||
|
||||
print(f"Fase 4 aplicada: {applied} renombres.")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,41 @@
|
||||
// DECISION DETERMINISTICA ESTRICTA (Create vs Update)
|
||||
// Llave: el ID de la opp de SUCURSAL (unico en GHL), guardado como custom field
|
||||
// "ID Oportunidad Sucursal" en la opp de Marca. Buscamos entre las opps del
|
||||
// contacto en Marca una cuyo custom field valga exactamente ese id.
|
||||
// - Si hay match -> UPDATE (esa opp exacta).
|
||||
// - Si NO hay match -> CREATE (sin fallback por nombre).
|
||||
|
||||
const result = $input.first().json;
|
||||
const opportunities = result.opportunities || [];
|
||||
const sucursalOppId = $('Datos de Lead').first().json.Oportunidad.opportunity_id;
|
||||
|
||||
let action = 'CREATE';
|
||||
let opportunityId = null;
|
||||
let matchReason = '';
|
||||
|
||||
const match = opportunities.find(o =>
|
||||
(o.customFields || []).some(cf => {
|
||||
const v = cf.fieldValueString ?? cf.fieldValue ?? '';
|
||||
return String(v) === String(sucursalOppId);
|
||||
})
|
||||
);
|
||||
|
||||
if (match) {
|
||||
action = 'UPDATE';
|
||||
opportunityId = match.id;
|
||||
matchReason = 'Match por ID Oportunidad Sucursal';
|
||||
} else {
|
||||
action = 'CREATE';
|
||||
matchReason = 'Sin match por ID Oportunidad Sucursal -> CREATE (contacto tiene ' + opportunities.length + ' opps)';
|
||||
}
|
||||
|
||||
return [{
|
||||
json: {
|
||||
action,
|
||||
opportunityId,
|
||||
matchReason,
|
||||
sucursalOppId,
|
||||
contactId: $('Set Contact ID Resuelto').first().json.contactId,
|
||||
totalOppsContacto: opportunities.length
|
||||
}
|
||||
}];
|
||||
@@ -0,0 +1,123 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""Tercer fix del path cascada del workflow Marca->Sucursal V2 (4UMRwxJdHFfOGHBp).
|
||||
|
||||
Tras arreglar el token y el locationId del 'Buscar Contacto Objetivo - SUCURSAL(mail)',
|
||||
el endpoint responde correctamente con {contacts:[], total:0} cuando no encuentra
|
||||
por email. Pero el IF siguiente `Si hay mas de un resultado` no maneja el caso
|
||||
0 resultados:
|
||||
|
||||
condiciones actuales: contacts[1] exists AND total > 0 (AND)
|
||||
true -> Retry telefono
|
||||
false -> Obtener Contacto Cuenta objetivo - SUCURSAL (BUG: con id undefined)
|
||||
|
||||
Cuando total=0, ambas condiciones son false, va a [false] -> `Obtener Contacto`
|
||||
con `$json.contacts[0].id` = undefined, lo que dispara 403 y termina en CREATE
|
||||
fallido (si hay duplicate phone) o CREATE exitoso (si es contacto nuevo).
|
||||
|
||||
Fix: alinear con la logica del IF `Si hay mas de un resultado1` (el siguiente
|
||||
en la cadena), que ya tiene la condicion correcta:
|
||||
|
||||
condiciones correctas: contacts[1] exists OR total equals 0 (OR)
|
||||
true (2+ resultados o 0 resultados) -> Retry telefono
|
||||
false (exactamente 1 resultado) -> Obtener Contacto
|
||||
|
||||
Cambio neto: 2 expressions (operator y combinator) en un solo IF. Total nodos
|
||||
sin cambios.
|
||||
"""
|
||||
import argparse
|
||||
import os
|
||||
import sys
|
||||
|
||||
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
|
||||
from scripts.n8n_workflow_lib import load_credentials, N8NClient
|
||||
|
||||
WID = "4UMRwxJdHFfOGHBp"
|
||||
TARGET_NODE = "Si hay más de un resultado"
|
||||
REFERENCE_NODE = "Si hay más de un resultado1" # ya tiene la logica correcta
|
||||
|
||||
|
||||
def parse_args():
|
||||
ap = argparse.ArgumentParser()
|
||||
ap.add_argument("--apply", action="store_true",
|
||||
help="Aplicar PUT real (default: dry-run a n8n/dryrun_*.json)")
|
||||
return ap.parse_args()
|
||||
|
||||
|
||||
def main():
|
||||
args = parse_args()
|
||||
api_key, base_url = load_credentials()
|
||||
client = N8NClient(api_key, base_url)
|
||||
|
||||
print(f"[1/6] GET workflow {WID}")
|
||||
wf = client.get_workflow(WID)
|
||||
prev_version = wf.get("versionId")
|
||||
print(f" versionId previo: {prev_version}")
|
||||
print(f" total nodos : {len(wf.get('nodes') or [])}")
|
||||
|
||||
target = next((n for n in wf["nodes"] if n.get("name") == TARGET_NODE), None)
|
||||
ref = next((n for n in wf["nodes"] if n.get("name") == REFERENCE_NODE), None)
|
||||
if not target or not ref:
|
||||
print(f"\nERROR: nodos requeridos no existen. Abortando.")
|
||||
sys.exit(2)
|
||||
|
||||
target_conds = target["parameters"]["conditions"]
|
||||
ref_conds = ref["parameters"]["conditions"]
|
||||
|
||||
print(f"\n[2/6] Diagnostico")
|
||||
print(f" Target combinator: {target_conds.get('combinator')}")
|
||||
print(f" Ref combinator: {ref_conds.get('combinator')}")
|
||||
for i, (tc, rc) in enumerate(zip(target_conds["conditions"], ref_conds["conditions"])):
|
||||
t_op = tc.get("operator", {}).get("operation")
|
||||
r_op = rc.get("operator", {}).get("operation")
|
||||
print(f" cond[{i}]: target.op={t_op} ref.op={r_op}")
|
||||
|
||||
# Aplicar misma estructura del ref al target (preservando los ids existentes)
|
||||
if target_conds.get("combinator") == "or":
|
||||
is_total_eq_zero = any(
|
||||
c.get("operator", {}).get("operation") == "equals" and c.get("leftValue", "").endswith("total }}")
|
||||
for c in target_conds["conditions"]
|
||||
)
|
||||
if is_total_eq_zero:
|
||||
print(f"\n [SKIP] Target ya esta arreglado.")
|
||||
return
|
||||
|
||||
print(f"\n[3/6] Backup fresco")
|
||||
_, bpath = client.backup_workflow(WID, label="pre_fix_cascada_if_zero_results")
|
||||
print(f" backup -> {bpath}")
|
||||
|
||||
print(f"\n[4/6] Aplicar cambio en memoria")
|
||||
# Forzar combinator OR
|
||||
target_conds["combinator"] = "or"
|
||||
# Cambiar la segunda condicion (total > 0) a (total equals 0)
|
||||
second = target_conds["conditions"][1]
|
||||
second["operator"]["operation"] = "equals"
|
||||
# rightValue ya era 0 (number), se queda igual
|
||||
print(f" nuevo combinator: {target_conds['combinator']}")
|
||||
print(f" nuevo cond[1].op: {second['operator']['operation']}")
|
||||
|
||||
print(f"\n[5/6] {'APPLY' if args.apply else 'DRY-RUN'} put_workflow")
|
||||
result = client.put_workflow(WID, wf, dry_run=not args.apply)
|
||||
if not args.apply:
|
||||
print(f" dry-run dump -> {result}")
|
||||
print("\nLISTO. Revisa el JSON. Si todo bien, corre con --apply.")
|
||||
return
|
||||
|
||||
print(f"\n[6/6] Verificar y reactivar")
|
||||
new_wf = client.verify_post(WID, prev_version_id=prev_version)
|
||||
target_post = next((n for n in new_wf["nodes"] if n.get("name") == TARGET_NODE), None)
|
||||
conds_post = target_post["parameters"]["conditions"]
|
||||
print(f" versionId nuevo : {new_wf.get('versionId')}")
|
||||
print(f" total nodos : {len(new_wf.get('nodes') or [])}")
|
||||
print(f" combinator post : {conds_post.get('combinator')}")
|
||||
print(f" cond[1].op post : {conds_post['conditions'][1]['operator']['operation']}")
|
||||
if not new_wf.get("active"):
|
||||
print(" workflow inactivo; reactivando...")
|
||||
client.activate(WID)
|
||||
new_wf = client.get_workflow(WID)
|
||||
print(f" active final : {new_wf.get('active')}")
|
||||
print("\nOK.")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,102 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""Segundo fix del nodo `Buscar Contacto Objetivo - SUCURSAL(mail)` del
|
||||
workflow Marca->Sucursal V2 (4UMRwxJdHFfOGHBp).
|
||||
|
||||
El primer fix (`_fix_cascada_token_mail.py`) corrigio el header Authorization
|
||||
para usar la referencia explicita a 'Datos API Cuenta objetivo - SUCURSAL'.
|
||||
Pero el body sigue con el mismo bug: usa `$json['ID LOCATION BUCEFALO']`
|
||||
(implicito), que resuelve a undefined porque el $json llega del IF
|
||||
`¿cfValue presente?` y no contiene ese campo. Resultado: 422 "locationId
|
||||
can't be undefined".
|
||||
|
||||
Patron correcto (usado por `Retry telefono` y `Retry nombre`):
|
||||
\"locationId\": \"{{ $('Datos API Cuenta objetivo - SUCURSAL').item.json['ID LOCATION BUCEFALO'] }}\"
|
||||
|
||||
Cambio neto: 1 expression en el jsonBody del nodo. Total nodos sin cambios.
|
||||
"""
|
||||
import argparse
|
||||
import os
|
||||
import sys
|
||||
|
||||
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
|
||||
from scripts.n8n_workflow_lib import load_credentials, N8NClient
|
||||
|
||||
WID = "4UMRwxJdHFfOGHBp"
|
||||
TARGET_NODE = "Buscar Contacto Objetivo - SUCURSAL(mail)"
|
||||
BUGGY_FRAGMENT = '"locationId": "{{ $json[\'ID LOCATION BUCEFALO\'] }}"'
|
||||
FIXED_FRAGMENT = (
|
||||
'"locationId": "{{ '
|
||||
"$('Datos API Cuenta objetivo - SUCURSAL').item.json['ID LOCATION BUCEFALO']"
|
||||
' }}"'
|
||||
)
|
||||
|
||||
|
||||
def parse_args():
|
||||
ap = argparse.ArgumentParser()
|
||||
ap.add_argument("--apply", action="store_true",
|
||||
help="Aplicar PUT real (default: dry-run a n8n/dryrun_*.json)")
|
||||
return ap.parse_args()
|
||||
|
||||
|
||||
def main():
|
||||
args = parse_args()
|
||||
api_key, base_url = load_credentials()
|
||||
client = N8NClient(api_key, base_url)
|
||||
|
||||
print(f"[1/6] GET workflow {WID}")
|
||||
wf = client.get_workflow(WID)
|
||||
prev_version = wf.get("versionId")
|
||||
print(f" versionId previo: {prev_version}")
|
||||
print(f" active : {wf.get('active')}")
|
||||
print(f" total nodos : {len(wf.get('nodes') or [])}")
|
||||
|
||||
target = next((n for n in wf["nodes"] if n.get("name") == TARGET_NODE), None)
|
||||
if not target:
|
||||
print(f"\nERROR: nodo {TARGET_NODE!r} no existe. Abortando.")
|
||||
sys.exit(2)
|
||||
current_body = (target.get("parameters") or {}).get("jsonBody", "")
|
||||
print(f"\n[2/6] Verificar body actual")
|
||||
print(f" actual: {current_body}")
|
||||
|
||||
if FIXED_FRAGMENT in current_body:
|
||||
print(f"\n [SKIP] El body ya esta arreglado.")
|
||||
return
|
||||
if BUGGY_FRAGMENT not in current_body:
|
||||
print(f"\nERROR: el body no contiene el fragmento buggy esperado.")
|
||||
print(f" buggy esperado: {BUGGY_FRAGMENT}")
|
||||
sys.exit(2)
|
||||
|
||||
print(f"\n[3/6] Backup fresco")
|
||||
_, bpath = client.backup_workflow(WID, label="pre_fix_cascada_locationid_mail")
|
||||
print(f" backup -> {bpath}")
|
||||
|
||||
print(f"\n[4/6] Aplicar cambio en memoria")
|
||||
new_body = current_body.replace(BUGGY_FRAGMENT, FIXED_FRAGMENT)
|
||||
target["parameters"]["jsonBody"] = new_body
|
||||
print(f" nuevo: {new_body}")
|
||||
|
||||
print(f"\n[5/6] {'APPLY' if args.apply else 'DRY-RUN'} put_workflow")
|
||||
result = client.put_workflow(WID, wf, dry_run=not args.apply)
|
||||
if not args.apply:
|
||||
print(f" dry-run dump -> {result}")
|
||||
print("\nLISTO. Revisa el JSON. Si todo bien, corre con --apply.")
|
||||
return
|
||||
|
||||
print(f"\n[6/6] Verificar y reactivar")
|
||||
new_wf = client.verify_post(WID, prev_version_id=prev_version)
|
||||
target_post = next((n for n in new_wf["nodes"] if n.get("name") == TARGET_NODE), None)
|
||||
body_post = (target_post.get("parameters") or {}).get("jsonBody", "")
|
||||
print(f" versionId nuevo : {new_wf.get('versionId')}")
|
||||
print(f" total nodos : {len(new_wf.get('nodes') or [])}")
|
||||
print(f" body coincide : {FIXED_FRAGMENT in body_post}")
|
||||
if not new_wf.get("active"):
|
||||
print(" workflow inactivo; reactivando...")
|
||||
client.activate(WID)
|
||||
new_wf = client.get_workflow(WID)
|
||||
print(f" active final : {new_wf.get('active')}")
|
||||
print("\nOK.")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,109 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""Fix del bug de token en la cascada del workflow Marca->Sucursal V2.
|
||||
|
||||
El nodo `Buscar Contacto Objetivo - SUCURSAL(mail)` (workflow 4UMRwxJdHFfOGHBp)
|
||||
usa una expression IMPLICITA para el header Authorization:
|
||||
=Bearer {{ $json['Token/API'] }}
|
||||
|
||||
Esto resuelve al token de MARCA (heredado del set node 'Datos API Cuenta Origen'),
|
||||
no al de sucursal, por lo que el endpoint POST /contacts/search responde 401.
|
||||
El nodo siguiente (`Obtener Contacto Cuenta objetivo - SUCURSAL`) recibe
|
||||
{error: ...} en lugar de {contacts: [...]} y termina rebotando con 403 porque
|
||||
$json.contacts[0].id es undefined.
|
||||
|
||||
Fix: cambiar la expression a la explicita usada por los otros 9 HTTP nodes:
|
||||
=Bearer {{ $('Datos API Cuenta objetivo - SUCURSAL').item.json['Token/API'] }}
|
||||
|
||||
Cambio neto: 1 expression. Total nodos sin cambios (42).
|
||||
Modo dry-run por defecto; --apply para PUT real + reactivar.
|
||||
"""
|
||||
import argparse
|
||||
import os
|
||||
import sys
|
||||
|
||||
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
|
||||
from scripts.n8n_workflow_lib import load_credentials, N8NClient
|
||||
|
||||
WID = "4UMRwxJdHFfOGHBp"
|
||||
TARGET_NODE = "Buscar Contacto Objetivo - SUCURSAL(mail)"
|
||||
BUGGY_VALUE = "=Bearer {{ $json['Token/API'] }}"
|
||||
FIXED_VALUE = "=Bearer {{ $('Datos API Cuenta objetivo - SUCURSAL').item.json['Token/API'] }}"
|
||||
|
||||
|
||||
def parse_args():
|
||||
ap = argparse.ArgumentParser()
|
||||
ap.add_argument("--apply", action="store_true",
|
||||
help="Aplicar PUT real (default: dry-run a n8n/dryrun_*.json)")
|
||||
return ap.parse_args()
|
||||
|
||||
|
||||
def main():
|
||||
args = parse_args()
|
||||
api_key, base_url = load_credentials()
|
||||
client = N8NClient(api_key, base_url)
|
||||
|
||||
print(f"[1/6] GET workflow {WID}")
|
||||
wf = client.get_workflow(WID)
|
||||
prev_version = wf.get("versionId")
|
||||
print(f" versionId previo: {prev_version}")
|
||||
print(f" active : {wf.get('active')}")
|
||||
print(f" total nodos : {len(wf.get('nodes') or [])}")
|
||||
|
||||
# Localizar nodo y validar expression actual (defensa contra cambios concurrentes)
|
||||
target = next((n for n in wf["nodes"] if n.get("name") == TARGET_NODE), None)
|
||||
if not target:
|
||||
print(f"\nERROR: nodo {TARGET_NODE!r} no existe. Abortando.")
|
||||
sys.exit(2)
|
||||
headers = ((target.get("parameters") or {}).get("headerParameters") or {}).get("parameters") or []
|
||||
auth_header = next((h for h in headers if h.get("name") == "Authorization"), None)
|
||||
if not auth_header:
|
||||
print(f"\nERROR: nodo {TARGET_NODE!r} no tiene header Authorization. Abortando.")
|
||||
sys.exit(2)
|
||||
current = auth_header.get("value", "")
|
||||
print(f"\n[2/6] Verificar expression actual")
|
||||
print(f" actual : {current}")
|
||||
print(f" esperada : {BUGGY_VALUE}")
|
||||
if current == FIXED_VALUE:
|
||||
print(f"\n [SKIP] La expression ya esta arreglada. Nada que hacer.")
|
||||
return
|
||||
if current != BUGGY_VALUE:
|
||||
print(f"\nERROR: expression actual difiere de la esperada y tampoco esta arreglada.")
|
||||
print(f" Abortando para no sobrescribir cambios manuales.")
|
||||
sys.exit(2)
|
||||
|
||||
print(f"\n[3/6] Backup fresco")
|
||||
_, bpath = client.backup_workflow(WID, label="pre_fix_cascada_token_mail")
|
||||
print(f" backup -> {bpath}")
|
||||
|
||||
print(f"\n[4/6] Aplicar cambio en memoria")
|
||||
auth_header["value"] = FIXED_VALUE
|
||||
print(f" nuevo: {FIXED_VALUE}")
|
||||
|
||||
print(f"\n[5/6] {'APPLY' if args.apply else 'DRY-RUN'} put_workflow")
|
||||
result = client.put_workflow(WID, wf, dry_run=not args.apply)
|
||||
if not args.apply:
|
||||
print(f" dry-run dump -> {result}")
|
||||
print("\nLISTO. Revisa el JSON. Si todo bien, corre con --apply.")
|
||||
return
|
||||
|
||||
print(f"\n[6/6] Verificar y reactivar")
|
||||
new_wf = client.verify_post(WID, prev_version_id=prev_version)
|
||||
# Re-verificar la expression
|
||||
target_post = next((n for n in new_wf["nodes"] if n.get("name") == TARGET_NODE), None)
|
||||
headers_post = ((target_post.get("parameters") or {}).get("headerParameters") or {}).get("parameters") or []
|
||||
auth_post = next((h for h in headers_post if h.get("name") == "Authorization"), None)
|
||||
print(f" versionId nuevo : {new_wf.get('versionId')}")
|
||||
print(f" total nodos : {len(new_wf.get('nodes') or [])} (esperado: 42)")
|
||||
print(f" expression post : {auth_post.get('value') if auth_post else '?'}")
|
||||
print(f" coincide con fix : {auth_post.get('value') == FIXED_VALUE if auth_post else False}")
|
||||
if not new_wf.get("active"):
|
||||
print(" workflow inactivo tras PUT; reactivando...")
|
||||
client.activate(WID)
|
||||
new_wf = client.get_workflow(WID)
|
||||
print(f" active final : {new_wf.get('active')}")
|
||||
print("\nOK.")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,92 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""Test puntual del fallback que insertamos:
|
||||
- Crea contacto en PINOTEPA SIN poblar el CF id_contacto_sucursal.
|
||||
- Dispara el webhook Sucursal->Marca.
|
||||
- Espera unos segundos.
|
||||
- Verifica que el CF en la SUCURSAL quedo poblado == contact.id.
|
||||
Eso prueba que el IF entro por la rama "false" y el PUT autoenlazo.
|
||||
|
||||
- Cleanup: borra el contacto sucursal y cualquier contacto Marca que se haya
|
||||
creado con CF apuntando al sucursal.
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
import json
|
||||
import datetime
|
||||
|
||||
ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
sys.path.insert(0, ROOT_DIR)
|
||||
sys.path.insert(0, os.path.join(ROOT_DIR, "scripts"))
|
||||
|
||||
# Importa helpers del harness E2E
|
||||
from scripts import n8n_e2e_test as t
|
||||
|
||||
PINOTEPA = t.PINOTEPA_LOCATION_ID
|
||||
BRAND = t.BRAND_LOCATION_ID
|
||||
WEBHOOK = t.WEBHOOK_URL_SUC_TO_MARCA
|
||||
CF_KEY = t.CF_KEY
|
||||
|
||||
|
||||
def log(msg):
|
||||
ts = datetime.datetime.now().strftime("%H:%M:%S")
|
||||
print(f"[{ts}] {msg}", flush=True)
|
||||
|
||||
|
||||
def main():
|
||||
cleanup = t.Cleanup()
|
||||
try:
|
||||
# 1) Resolver field_id del CF en sucursal (varia por sucursal)
|
||||
cf_id_branch = t.resolve_cf_id_in_location(PINOTEPA, CF_KEY)
|
||||
log(f"CF field_id en PINOTEPA: {cf_id_branch}")
|
||||
assert cf_id_branch, "No se resolvio field_id del CF en sucursal"
|
||||
|
||||
# 2) Crear contacto sucursal SIN CF poblado
|
||||
phone = "+5219991119911"
|
||||
payload = t._new_contact_payload("FB", phone=phone, email="fbtest@e3.local")
|
||||
branch_id = t.create_contact(PINOTEPA, payload)
|
||||
cleanup.register(PINOTEPA, branch_id, "fallback branch")
|
||||
log(f"Branch contact creado: {branch_id}")
|
||||
|
||||
# 3) Sanity: confirmar que el CF arranca VACIO en sucursal
|
||||
c0 = t.get_contact(PINOTEPA, branch_id)
|
||||
cf0 = t.cf_value_of(c0, cf_id_branch)
|
||||
log(f"CF pre-webhook en sucursal: {cf0!r} (esperado: vacio)")
|
||||
assert not cf0, f"CF deberia estar vacio antes del webhook, valor={cf0!r}"
|
||||
|
||||
# 4) Disparar webhook Sucursal -> Marca
|
||||
webhook_payload = t.build_webhook_payload_suc_to_marca(c0)
|
||||
s, body = t.fire_webhook(WEBHOOK, webhook_payload)
|
||||
log(f"Webhook status: {s} body: {body[:100]}")
|
||||
assert s == 200, f"Webhook fallo: {s}"
|
||||
|
||||
# 5) Esperar a que el workflow termine + GHL indexe
|
||||
log("Esperando 15s para que el fallback PUT corra y GHL indexe...")
|
||||
time.sleep(15)
|
||||
|
||||
# 6) Verificar que el CF en SUCURSAL quedo poblado con branch_id
|
||||
c1 = t.get_contact(PINOTEPA, branch_id)
|
||||
cf1 = t.cf_value_of(c1, cf_id_branch)
|
||||
log(f"CF post-webhook en sucursal: {cf1!r}")
|
||||
|
||||
if cf1 == branch_id:
|
||||
log("[OK] El fallback autoenlazo correctamente: rama false -> PUT funciono.")
|
||||
else:
|
||||
log(f"[FAIL] CF post-webhook = {cf1!r}, esperaba {branch_id!r}")
|
||||
sys.exit(2)
|
||||
|
||||
# 7) Buscar contacto Marca creado (deberia existir con CF apuntando)
|
||||
brand_matches = t._find_brand_with_cf(branch_id)
|
||||
if brand_matches:
|
||||
for m in brand_matches:
|
||||
cleanup.register(BRAND, m["id"], "fallback brand (creado por workflow)")
|
||||
log(f"[OK] Brand creado: {[m['id'] for m in brand_matches]}")
|
||||
else:
|
||||
log("[WARN] No se encontro contacto Marca con CF=branch_id (puede tardar mas en indexar).")
|
||||
|
||||
finally:
|
||||
cleanup.run()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,120 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""Test puntual del fallback post-UPDATE en workflow Marca->Sucursal V2.
|
||||
|
||||
Escenario:
|
||||
- Crear contacto en SUCURSAL (con CF autoref).
|
||||
- Crear contacto en MARCA SIN CF, mismo phone.
|
||||
- Disparar webhook Marca->Sucursal.
|
||||
- El workflow va por cascada phone, encuentra contacto sucursal, UPDATE.
|
||||
- Tras UPDATE, el nuevo IF detecta cfValue vacio -> autoenlace.
|
||||
- Verificar:
|
||||
* CF en MARCA queda poblado con id del contacto sucursal.
|
||||
* CF en SUCURSAL queda con autoref.
|
||||
- Cleanup.
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
import datetime
|
||||
|
||||
ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
sys.path.insert(0, ROOT_DIR)
|
||||
sys.path.insert(0, os.path.join(ROOT_DIR, "scripts"))
|
||||
|
||||
from scripts import n8n_e2e_test as t
|
||||
|
||||
|
||||
def log(msg):
|
||||
ts = datetime.datetime.now().strftime("%H:%M:%S")
|
||||
print(f"[{ts}] {msg}", flush=True)
|
||||
|
||||
|
||||
def main():
|
||||
cleanup = t.Cleanup()
|
||||
try:
|
||||
phone = "+5219991122334"
|
||||
email = "fbpost@e3.local"
|
||||
|
||||
# 1) Resolver field_id CF en sucursal (varia por sucursal)
|
||||
cf_id_branch = t.resolve_cf_id_in_location(t.PINOTEPA_LOCATION_ID, t.CF_KEY)
|
||||
log(f"CF field_id en PINOTEPA: {cf_id_branch}")
|
||||
|
||||
# 2) Crear contacto SUCURSAL con autoref CF
|
||||
payload_branch = t._new_contact_payload("FBPU", phone=phone, email=email)
|
||||
branch_id = t.create_contact(t.PINOTEPA_LOCATION_ID, payload_branch)
|
||||
cleanup.register(t.PINOTEPA_LOCATION_ID, branch_id, "fbpost branch")
|
||||
log(f"Branch creado: {branch_id}")
|
||||
# Poblar CF autoref en sucursal
|
||||
t.update_contact(t.PINOTEPA_LOCATION_ID, branch_id, {
|
||||
"customFields": [
|
||||
{"id": cf_id_branch, "key": t.CF_KEY, "field_value": branch_id}
|
||||
]
|
||||
})
|
||||
|
||||
# 2b) Esperar a que GHL indexe el email en sucursal (el workflow va por cascada mail)
|
||||
log("Esperando indexacion del email en sucursal...")
|
||||
suc_token = t.get_token(t.PINOTEPA_LOCATION_ID)
|
||||
import time as _t
|
||||
deadline = _t.time() + 30
|
||||
indexed = False
|
||||
while _t.time() < deadline:
|
||||
try:
|
||||
r = t.gc._request("POST", "/contacts/search", suc_token, json={
|
||||
"locationId": t.PINOTEPA_LOCATION_ID, "pageLimit": 5, "query": email
|
||||
})
|
||||
if any(c.get("id") == branch_id for c in (r.get("contacts") or [])):
|
||||
indexed = True
|
||||
break
|
||||
except Exception:
|
||||
pass
|
||||
_t.sleep(2)
|
||||
log(f" email indexado: {indexed}")
|
||||
assert indexed, "El email no se indexo en sucursal a tiempo"
|
||||
|
||||
# 3) Crear contacto MARCA SIN CF, mismo phone
|
||||
payload_brand = t._new_contact_payload("FBPU", phone=phone, email=email)
|
||||
brand_id = t.create_contact(t.BRAND_LOCATION_ID, payload_brand)
|
||||
cleanup.register(t.BRAND_LOCATION_ID, brand_id, "fbpost brand")
|
||||
log(f"Brand creado (sin CF): {brand_id}")
|
||||
|
||||
# 4) Sanity: confirmar CF Marca vacio
|
||||
cm0 = t.get_contact(t.BRAND_LOCATION_ID, brand_id)
|
||||
cf_brand_0 = t.cf_value_of(cm0, t.CF_ID_BRAND)
|
||||
log(f"CF Marca pre-webhook: {cf_brand_0!r} (esperado: vacio)")
|
||||
assert not cf_brand_0, f"CF Marca deberia estar vacio, valor={cf_brand_0!r}"
|
||||
|
||||
# 5) Disparar webhook Marca->Sucursal
|
||||
brand_obj = t.get_contact(t.BRAND_LOCATION_ID, brand_id)
|
||||
webhook_payload = t.build_webhook_payload_marca_to_suc(brand_obj)
|
||||
s, body = t.fire_webhook(t.WEBHOOK_URL_MARCA_TO_SUC, webhook_payload)
|
||||
log(f"Webhook status: {s} body: {body[:100]}")
|
||||
assert s == 200, f"Webhook fallo: {s}"
|
||||
|
||||
log("Esperando 18s para que el workflow complete (cascada + UPDATE + autoenlace)...")
|
||||
time.sleep(18)
|
||||
|
||||
# 6) Verificar: CF en MARCA ahora poblado con branch_id
|
||||
cm1 = t.get_contact(t.BRAND_LOCATION_ID, brand_id)
|
||||
cf_brand_1 = t.cf_value_of(cm1, t.CF_ID_BRAND)
|
||||
log(f"CF Marca post-webhook: {cf_brand_1!r}")
|
||||
if cf_brand_1 == branch_id:
|
||||
log("[OK] Fallback post-UPDATE: CF Marca quedo enlazado al branch_id.")
|
||||
else:
|
||||
log(f"[FAIL] CF Marca = {cf_brand_1!r}, esperaba {branch_id!r}")
|
||||
sys.exit(2)
|
||||
|
||||
# 7) Verificar: CF en SUCURSAL sigue siendo autoref (era branch_id desde antes)
|
||||
cb1 = t.get_contact(t.PINOTEPA_LOCATION_ID, branch_id)
|
||||
cf_branch_1 = t.cf_value_of(cb1, cf_id_branch)
|
||||
log(f"CF Sucursal post-webhook: {cf_branch_1!r}")
|
||||
if cf_branch_1 == branch_id:
|
||||
log("[OK] CF Sucursal mantiene autoref.")
|
||||
else:
|
||||
log(f"[WARN] CF Sucursal cambio a {cf_branch_1!r}")
|
||||
|
||||
finally:
|
||||
cleanup.run()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,106 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""One-off: agrega filtro `TIPO DE TIENDA != NO DIGITAL` (field 7279) al nodo
|
||||
Baserow de lookup del Verificador (tabla 750) en los 3 workflows que lo leen.
|
||||
|
||||
Operador confirmado empíricamente contra Baserow REST: `not_equal` (2026-05-30).
|
||||
Idempotente (no duplica el filtro). Backup por workflow. Dry-run por defecto.
|
||||
|
||||
python n8n/add_tipo_de_tienda_filter.py # dry-run
|
||||
python n8n/add_tipo_de_tienda_filter.py --apply # aplica + activa + verifica
|
||||
"""
|
||||
import argparse
|
||||
import copy
|
||||
import os
|
||||
import sys
|
||||
|
||||
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
if ROOT not in sys.path:
|
||||
sys.path.insert(0, ROOT)
|
||||
|
||||
from scripts.n8n_workflow_lib import load_credentials, N8NClient, PutFailedError # noqa: E402
|
||||
|
||||
FILTER_FIELD = 7279
|
||||
FILTER = {"field": FILTER_FIELD, "operator": "not_equal", "value": "NO DIGITAL"}
|
||||
|
||||
# (workflow_id, nodo_objetivo, rol)
|
||||
TARGETS = [
|
||||
("4UMRwxJdHFfOGHBp", "Obtener Info de cuenta objetivo - SUCURSAL", "[1604] CRITICO (SUCURSAL contains)"),
|
||||
("ddUEORBEtZLzsQF2", "Buscar Sucursal en Verificador de Sucursales", "[2004] defensa (SC BUCEFALO)"),
|
||||
("EuPdIkCORyh0skoB", "Buscar Cuenta Sucursal Bucefalo", "[SUCURSAL] defensa (SC BUCEFALO)"),
|
||||
]
|
||||
|
||||
|
||||
def get_fields_list(node):
|
||||
"""Devuelve (fields_list, ok). fields_list es la lista mutable additionalOptions.filters.fields."""
|
||||
params = node.get("parameters") or {}
|
||||
ao = params.get("additionalOptions") or {}
|
||||
filt = ao.get("filters") or {}
|
||||
fields = filt.get("fields")
|
||||
return fields if isinstance(fields, list) else None
|
||||
|
||||
|
||||
def main():
|
||||
ap = argparse.ArgumentParser()
|
||||
ap.add_argument("--apply", action="store_true")
|
||||
args = ap.parse_args()
|
||||
dry = not args.apply
|
||||
client = N8NClient(*load_credentials())
|
||||
|
||||
for wid, node_name, rol in TARGETS:
|
||||
print("\n" + "=" * 70)
|
||||
print(f"{wid} {rol}\n nodo: {node_name!r}")
|
||||
wf = client.get_workflow(wid)
|
||||
node = client.find_node(wf, node_name)
|
||||
if node is None:
|
||||
print(f" ABORTA: nodo no encontrado. Nodos Baserow disponibles:")
|
||||
for n in wf.get("nodes", []):
|
||||
if n.get("type") == "n8n-nodes-base.baserow":
|
||||
print(f" - {n['name']!r}")
|
||||
sys.exit(1)
|
||||
fields = get_fields_list(node)
|
||||
if fields is None:
|
||||
print(" ABORTA: el nodo no tiene additionalOptions.filters.fields (estructura inesperada)")
|
||||
sys.exit(1)
|
||||
existing = [f for f in fields if f.get("field") == FILTER_FIELD]
|
||||
print(f" filtros actuales: {fields}")
|
||||
if existing:
|
||||
print(f" YA tiene filtro field {FILTER_FIELD} -> idempotente, skip. active={wf.get('active')}")
|
||||
continue
|
||||
|
||||
if not dry:
|
||||
_, bpath = client.backup_workflow(wid, label="add_tipo_filter")
|
||||
print(f" backup -> {bpath}")
|
||||
wf2 = copy.deepcopy(wf)
|
||||
node2 = client.find_node(wf2, node_name)
|
||||
get_fields_list(node2).append(dict(FILTER))
|
||||
|
||||
res = client.put_workflow(wid, wf2, dry_run=dry)
|
||||
if dry:
|
||||
print(f" DRY-RUN payload -> {res['path']} (nodos={res['node_count']})")
|
||||
print(f" filtros que QUEDARIAN: {get_fields_list(node2)}")
|
||||
continue
|
||||
|
||||
# apply ya hecho por put_workflow(dry_run=False)
|
||||
print(f" PUT aplicado.")
|
||||
was_active = bool(wf.get("active"))
|
||||
if was_active:
|
||||
client.activate(wid)
|
||||
# verify
|
||||
wf_live = client.get_workflow(wid)
|
||||
node_live = client.find_node(wf_live, node_name)
|
||||
live_fields = get_fields_list(node_live) or []
|
||||
has = any(f.get("field") == FILTER_FIELD and f.get("operator") == "not_equal" for f in live_fields)
|
||||
print(f" VERIFY: filtro 7279 presente={has} active={wf_live.get('active')} filtros={live_fields}")
|
||||
if not has:
|
||||
print(" !! FALLO verificación — revisar / revertir")
|
||||
sys.exit(1)
|
||||
if was_active and not wf_live.get("active"):
|
||||
print(" !! quedó INACTIVO — reintentar activate")
|
||||
sys.exit(1)
|
||||
|
||||
print("\n" + ("DRY-RUN OK (sin cambios)" if dry else "APLICADO + VERIFICADO en los 3 workflows"))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,48 @@
|
||||
name,location_id,email,phone
|
||||
0001 - MP -Qro DEMO,Z64WQKORPVwXb5mn68Ef,,+5254426575426
|
||||
85930 - MP - TULYEHUALCO,rQYjjwsGnjEGagskOxix,,+525553535353
|
||||
85931 - MP - Marina Nacional,HvDw9Eg3rjrwkbQJXqfi,,
|
||||
85932 - MP - La Viga,fKn9SaXZoKcjjLryg10v,,+52123456789
|
||||
85933 - MP - CUAJIMALPA,VwDgbGbahFXPSyZmpCzt,ezstore85933@ezcorp.com,+525554959595
|
||||
85934 - MP - Atizapán,IE7ci6Ddfk1WvQabEa4q,,520123456789
|
||||
85935 - MP - Pilares,uZnMH5bO6MXTHcgHeyq9,,521234567890
|
||||
85937 - MP - METEPEC,NSDniGzjxotVDNa5YxqW,,5556565656
|
||||
85938 - MP - SENDERO,UsHXqoj2l6ND7Uc7sEo2,,+525557575757
|
||||
85939 - MP - Independencia ,RLAs9sQwbW2DOwzrTMYI,,+52 722 585 2063
|
||||
85940 - MP - Isidro Fabela,clhDZ0hIllKfV0AcgW53,,520123456789
|
||||
85941 - MP - Grand Plaza Toluca,pMPs9M4RaGJvWwfIFVIo,,520123465789
|
||||
85942 - MP - ZINACANTEPEC,MJU2fZ5VxQfHNgEfEQkg,ezstore85942@ezcorp.com,+525554545454
|
||||
85943 - MP - ATLACOMULCO,XzQ5Wi3RqHwc3AyE8QrI,ezstore85943@ezcorp.com,+52555559562
|
||||
85944 - MP - ZITACUARO,VYxTksrNuhmw9yL0Su2V,ezstore85944@ezcorp.com,+525556565656
|
||||
85945 - MP - Cuautla,arEgADUgzqjK3qH7LXSz,ezstore85945@ezcorp.com,+527351513869
|
||||
85946 - MP - Jojutla,5qebe8IhFUjlcBvJcqCa,,520123456789
|
||||
85947 - MP - Chilpancingo,qe3s72MRDhbEWlaFZ2Ko,,520123456789
|
||||
85948 - MP - MIACATLAN,RWqOypPx7S6t7MCleu2K,ezstore85948@ezcorp.com,+525559595959
|
||||
85949 - MP - ZACATEPEC,U0S0QntXgSOz9Fx18Db4,ezstore85949@ezcorp.com,+525553535353
|
||||
85950 - MP - Temixco,yjqKxoO02rsdwdJZSPmD,,520123456789
|
||||
85952 - MP - ECATEPEC,jB8BGwt9NoMAd3NGKjai,ezstore85952@ezcorp.com,+525556565656
|
||||
85953 - MP - Texcoco,vuPH36qujg6dSf92P5p2,,052123456789
|
||||
85954 - MP - Izcalli,r0fiuXv6zQnFyXJW2SWU,,520123456789
|
||||
85955 - MP - IXMIQUILPAN,ZvVhdvqBU5K9YLEH0fy3,ezstore85955@ezcorp.com,+525554545454
|
||||
85956 - MP - PLAZA EL SALADO,WLPVTRxg7W074dfzBxZL,ezstore85932@ezcorp.com,+525556565656
|
||||
85957 - MP - PINOTEPA,7H91g95hhLKwIUqSk0Rg,,0995682336
|
||||
85958 - MP - POCHUTLA,HvyNhH2IOe9ByeZrRo0N,ezstore85958@ezcorp.com,+525559595959
|
||||
85959 - MP - MIAHUATLAN,YGOlLwAl2AeN9JtZ47vV,ezstore85959@ezcorp.com,+525557575757
|
||||
85960 - MP - Cd. Carmen,XkduzafvwsrWcEFg6Qlj,,520123456789
|
||||
85961 - MP - VILLAS DEL SOL,nRSeOhlhQ3vyirTKYhPi,ezstore85961@ezcorp.com,+525558585858
|
||||
85962 - MP - Tuxtla 3,y9nelIq8hkrfdCQKK72o,,520123456789
|
||||
85963 - MP - TAPACHULA,ts3oTud0rw1Iat02zBAi,ezstore85963@ezcorp.com,+525551515151
|
||||
85964 - MP - Morelia 1,jE41bVhhnb5T505BFm4F,,520123456789
|
||||
85966 - MP - Uruapan,FoQWuksh4wQjPbVVZ8ZQ,,520123456789
|
||||
85967 - MP - Altamira,w1mYacmbTLjVwVDFF5Jx,,052123456789
|
||||
85968 - MP - HUAUCHINANGO,gtGA3sLqKBNSIAuf5hjx,ezstore85968@ezcorp.com,+525554545454
|
||||
85969 - MP - Tampico,WCHyow6KpjLFYriQWdbJ,,520123456789
|
||||
85970 - MP - REYNOSA,eJq6hneY4n7m0WYGcN42,ezstore85970@ezcorp.com,+525551515151
|
||||
85971 - MP - Satélite,R34lUVVpltnB8Z1RqnEB,,520123456789
|
||||
85973 - MP - Puebla,KEZ7dAhgwzK4uZfMvZuj,,520123456789
|
||||
85974 - MP - Eugenia,nF1uEaYB3mCK5em9bPn2,,+520123456789
|
||||
85975 - MP - Querétaro,blRZ21GlzgUCA7bl2uVw,,520123456789
|
||||
85976 - MP - Cancún,uJEn2iuUficuml9zxAnt,,520123456798
|
||||
85977 - MP - Interlomas,2eJPAdEGjC7iPhDDAeoy,,520123456789
|
||||
Monte Providencia,GbKkBpCmKu2QmloKFHy3,atencion.clientes@alsova.com,+527225852063
|
||||
Monte Providencia DEMO,Vf7qQl3L9vakJ8hDtQ8e,,4422874740
|
||||
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,855 @@
|
||||
{
|
||||
"name": "[2004][Monte Providencia] Actualizar \"contact.sucursal\", \"contact.tienda\" al crear contacto",
|
||||
"nodes": [
|
||||
{
|
||||
"parameters": {
|
||||
"content": "# De Sucursal a Marca",
|
||||
"color": 5
|
||||
},
|
||||
"type": "n8n-nodes-base.stickyNote",
|
||||
"position": [
|
||||
-2288,
|
||||
-320
|
||||
],
|
||||
"typeVersion": 1,
|
||||
"id": "d91325e1-1763-486c-a2c9-8cfffddd57b0",
|
||||
"name": "Sticky Note3"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"databaseId": 63,
|
||||
"tableId": 749,
|
||||
"additionalOptions": {
|
||||
"filters": {
|
||||
"fields": [
|
||||
{
|
||||
"field": 7235,
|
||||
"value": "={{ $('Datos de Lead').item.json.Sucursal['Cuenta Bucéfalo'] }}"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"type": "n8n-nodes-base.baserow",
|
||||
"typeVersion": 1,
|
||||
"position": [
|
||||
-912,
|
||||
-320
|
||||
],
|
||||
"id": "50da84c1-c2d3-4a36-91d3-e3745edccdc6",
|
||||
"name": "Obtener Info de cuenta origen - SUCURSAL",
|
||||
"credentials": {
|
||||
"baserowApi": {
|
||||
"id": "LZztQ3WMpzXjSTIH",
|
||||
"name": "Baserow account"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"url": "=https://services.leadconnectorhq.com/locations/{{ $('Datos API Cuenta Origen - SUCURSAL').item.json['Location ID'] }}/customFields",
|
||||
"sendHeaders": true,
|
||||
"headerParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "Accept",
|
||||
"value": "application/json"
|
||||
},
|
||||
{
|
||||
"name": "Version",
|
||||
"value": "2021-07-28"
|
||||
},
|
||||
{
|
||||
"name": "Authorization",
|
||||
"value": "=Bearer {{ $('Datos API Cuenta Origen - SUCURSAL').item.json['Token/API'] }}"
|
||||
}
|
||||
]
|
||||
},
|
||||
"options": {
|
||||
"redirect": {
|
||||
"redirect": {}
|
||||
}
|
||||
}
|
||||
},
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"typeVersion": 4.2,
|
||||
"position": [
|
||||
-224,
|
||||
-320
|
||||
],
|
||||
"id": "a0c27c4c-0f20-4bab-bf6b-bcdd8281a92b",
|
||||
"name": "Conseguir Custom Cuenta Origen- SUCURSAL"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"conditions": {
|
||||
"options": {
|
||||
"caseSensitive": true,
|
||||
"leftValue": "",
|
||||
"typeValidation": "strict",
|
||||
"version": 3
|
||||
},
|
||||
"conditions": [
|
||||
{
|
||||
"id": "44d54b9e-d192-4b54-bf0c-156b79afc6e2",
|
||||
"leftValue": "={{ $json.Cliente.Email }}",
|
||||
"rightValue": "@ezcorp.com",
|
||||
"operator": {
|
||||
"type": "string",
|
||||
"operation": "notContains"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "64f2add6-506c-4950-8026-c04c9547aeeb",
|
||||
"leftValue": "",
|
||||
"rightValue": "",
|
||||
"operator": {
|
||||
"type": "string",
|
||||
"operation": "equals",
|
||||
"name": "filter.operator.equals"
|
||||
}
|
||||
}
|
||||
],
|
||||
"combinator": "and"
|
||||
},
|
||||
"options": {}
|
||||
},
|
||||
"type": "n8n-nodes-base.if",
|
||||
"typeVersion": 2.3,
|
||||
"position": [
|
||||
-1392,
|
||||
-304
|
||||
],
|
||||
"id": "d68558b5-52b8-46b1-a359-fd956c7edc09",
|
||||
"name": "Omitir @ezcorp.com"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"assignments": {
|
||||
"assignments": [
|
||||
{
|
||||
"id": "8a998fd4-2de6-4895-ab3d-e052e823d1b8",
|
||||
"name": "Cliente.Fuente Posible Cliente",
|
||||
"value": "={{ $json.body['Fuente de Posible cliente'] }}",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"id": "938c6fec-ae16-4e7a-ba2a-f450794fa40d",
|
||||
"name": "Cliente.Fecha de creación",
|
||||
"value": "={{ $json.body.date_created }}",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"id": "b56a1939-2608-47c8-85ad-b60b557d2a27",
|
||||
"name": "Cliente.Sucursal.Sucursal",
|
||||
"value": "={{ $json.body.Sucursal }}",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"id": "0d07b9c9-4450-497b-ab81-3baa441787fb",
|
||||
"name": "Vehiculo.Versión.Versión",
|
||||
"value": "={{ $json.body['Version del Vehiculo'] }}",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"id": "75e3f337-00d1-429d-8d5b-85ec18e6c5e4",
|
||||
"name": "Vehiculo.Marca.Marca",
|
||||
"value": "={{ $json.body['Marca del Vehiculo'] }}",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"id": "cb09c536-fe84-4598-aaae-aa79f2eda61b",
|
||||
"name": "Vehiculo.Marca.fieldKey",
|
||||
"value": "=contact.marca_del_vehiculo",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"id": "17d36409-4c54-48dd-8100-f7f667fd2415",
|
||||
"name": "Vehiculo.Año.Año",
|
||||
"value": "={{ $json.body['Año del Vehículo'] }}",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"id": "a1886afc-b0af-4950-9752-f8bfff594896",
|
||||
"name": "Vehiculo.Modalidad.Modalidad",
|
||||
"value": "={{ $json.body['¿Qué modalidad prefieres?'] }}",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"id": "33b2c28a-1ad3-4c74-917f-3cd718a3a709",
|
||||
"name": "Cliente.Nombre",
|
||||
"value": "={{ $json.body['Información Adicional'] }}",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"id": "b36131ac-2d88-41f8-8f0f-7640cdb02b57",
|
||||
"name": "Cliente.Apellido",
|
||||
"value": "={{ $json.body.first_name }}",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"id": "ae252c8f-f0a1-41d9-a21d-04ca949f01c8",
|
||||
"name": "Cliente.Nombre Completo",
|
||||
"value": "={{ $json.body.full_name }}",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"id": "342a9377-0ded-4f23-b93b-1f76e57c0cbd",
|
||||
"name": "Cliente.Email",
|
||||
"value": "={{ $json.body.email }}",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"id": "cf1b7058-96c2-4c73-9250-719a88b68673",
|
||||
"name": "Cliente.Telefono",
|
||||
"value": "={{ $json.body.phone }}",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"id": "0b916193-15e8-4a91-9ff2-0a6f262b3c38",
|
||||
"name": "Cliente.Contact ID",
|
||||
"value": "={{ $json.body.contact_id }}",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"id": "67eeaa9b-f703-4521-82af-9d2797131edc",
|
||||
"name": "Cliente.Sucursal.fieldKey",
|
||||
"value": "contact.sucursal",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"id": "b05ea7bb-bbaa-467b-8247-eabb162ff029",
|
||||
"name": "Vehiculo.Versión.fieldKey",
|
||||
"value": "contact.version_del_vehiculo",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"id": "9891b919-ef4c-46bd-8414-6d916565d896",
|
||||
"name": "Vehiculo.Año.fieldKey",
|
||||
"value": "contact.ano_del_vehiculo",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"id": "0b6bf582-49c1-41de-9c2e-6ae85c4e41e8",
|
||||
"name": "Vehiculo.Modalidad.fieldKey",
|
||||
"value": "contact.que_modalidad_prefieres",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"id": "57199999-2d9b-41c7-8269-5a8183ca8132",
|
||||
"name": "Cliente.Cuándo necesitas el dinero",
|
||||
"value": "={{ $json.body[\"¿Cuándo necesitas el dinero?\"] }}",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"id": "8060d06e-b1da-4a65-8a0a-3c21d237d77e",
|
||||
"name": "Sucursal.Cuenta Bucéfalo",
|
||||
"value": "={{ $json.body.location.name }}",
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"options": {}
|
||||
},
|
||||
"type": "n8n-nodes-base.set",
|
||||
"typeVersion": 3.4,
|
||||
"position": [
|
||||
-1568,
|
||||
-304
|
||||
],
|
||||
"id": "d0e455ac-7ccc-4c55-bda2-1cd22092e2d2",
|
||||
"name": "Datos de Lead"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"assignments": {
|
||||
"assignments": [
|
||||
{
|
||||
"id": "55ff7d12-17b9-4bec-a324-e633020b131d",
|
||||
"name": "Name Location",
|
||||
"value": "={{ $('Obtener Info de cuenta origen - SUCURSAL').item.json.Nombre }}",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"id": "d877c8cd-db32-4c16-96dd-4eeb2dc48efe",
|
||||
"name": "Location ID",
|
||||
"value": "={{ $('Obtener Info de cuenta origen - SUCURSAL').item.json.Location_ID }}",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"id": "7698f395-5db8-415b-919e-3ad61c6566f8",
|
||||
"name": "Token/API",
|
||||
"value": "={{ $('Obtener Info de cuenta origen - SUCURSAL').item.json.API_token }}",
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"options": {}
|
||||
},
|
||||
"type": "n8n-nodes-base.set",
|
||||
"typeVersion": 3.4,
|
||||
"position": [
|
||||
-720,
|
||||
-320
|
||||
],
|
||||
"id": "f0cff3ec-8464-45d4-9e64-713c36e247c6",
|
||||
"name": "Datos API Cuenta Origen - SUCURSAL",
|
||||
"notes": "Esta en modo prueba forzada para Queretaro"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"content": "# CUENTA ORIGEN",
|
||||
"height": 240,
|
||||
"width": 1744
|
||||
},
|
||||
"type": "n8n-nodes-base.stickyNote",
|
||||
"position": [
|
||||
-976,
|
||||
-384
|
||||
],
|
||||
"typeVersion": 1,
|
||||
"id": "16a74d85-1e6c-4fc7-b854-580a2d3827a0",
|
||||
"name": "Sticky Note"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"url": "=https://services.leadconnectorhq.com/contacts/{{ $('Webhook').item.json.body.contact_id }}",
|
||||
"sendHeaders": true,
|
||||
"headerParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "Accept",
|
||||
"value": "application/json"
|
||||
},
|
||||
{
|
||||
"name": "Version",
|
||||
"value": "2021-07-28"
|
||||
},
|
||||
{
|
||||
"name": "Authorization",
|
||||
"value": "=Bearer {{ $json['Token/API'] }}"
|
||||
}
|
||||
]
|
||||
},
|
||||
"options": {
|
||||
"redirect": {
|
||||
"redirect": {}
|
||||
}
|
||||
}
|
||||
},
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"typeVersion": 4.4,
|
||||
"position": [
|
||||
-400,
|
||||
-320
|
||||
],
|
||||
"id": "92f33c6e-ee64-409d-8c60-9fbfe48b3265",
|
||||
"name": "Obtener Contacto Cuenta Origen - SUCURSAL"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"content": "",
|
||||
"height": 176,
|
||||
"width": 608,
|
||||
"color": 7
|
||||
},
|
||||
"type": "n8n-nodes-base.stickyNote",
|
||||
"position": [
|
||||
-448,
|
||||
-336
|
||||
],
|
||||
"typeVersion": 1,
|
||||
"id": "768a4001-5109-493f-b12e-e8ba5d30ec2f",
|
||||
"name": "Sticky Note1"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"method": "PUT",
|
||||
"url": "=https://services.leadconnectorhq.com/contacts/{{ $('Datos de Lead').item.json.Cliente['Contact ID'] }}",
|
||||
"sendHeaders": true,
|
||||
"headerParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "Accept",
|
||||
"value": "application/json"
|
||||
},
|
||||
{
|
||||
"name": "Version",
|
||||
"value": "2021-07-28"
|
||||
},
|
||||
{
|
||||
"name": "Authorization",
|
||||
"value": "=Bearer {{ $json['SC TOKEN BUCEFALO'] }}"
|
||||
}
|
||||
]
|
||||
},
|
||||
"sendBody": true,
|
||||
"specifyBody": "json",
|
||||
"jsonBody": "={\n \"customFields\": [\n {\n \"id\": \"{{ $('Buscar \"contact.sucursal\" y \"contact.tienda\"').item.json.sucursal.id }}\",\n \"key\": \"contact.sucursal\",\n \"field_value\": \"{{ $json.SUCURSAL }}\"\n },\n {\n \"id\": \"{{ $('Buscar \"contact.sucursal\" y \"contact.tienda\"').item.json.tienda.id }}\",\n \"key\": \"contact.tienda\",\n \"field_value\": \"{{ $json.TIENDA }}\"\n }\n ]\n}",
|
||||
"options": {
|
||||
"redirect": {
|
||||
"redirect": {}
|
||||
}
|
||||
}
|
||||
},
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"typeVersion": 4.2,
|
||||
"position": [
|
||||
512,
|
||||
-320
|
||||
],
|
||||
"id": "794ce5bb-dea0-4720-958a-1f0940c79e6d",
|
||||
"name": "Actualizar Contacto Cuenta Objetivo - SUCURSAL"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"httpMethod": "POST",
|
||||
"path": "8d574598-d977-4052-823a-26def39c6a64",
|
||||
"options": {}
|
||||
},
|
||||
"type": "n8n-nodes-base.webhook",
|
||||
"typeVersion": 2.1,
|
||||
"position": [
|
||||
-1776,
|
||||
-304
|
||||
],
|
||||
"id": "d4312610-e978-424c-a8a0-426026c4d4f6",
|
||||
"name": "Webhook",
|
||||
"webhookId": "8d574598-d977-4052-823a-26def39c6a64"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"jsCode": "const customFields = $input.first().json.customFields;\n\nfunction findField(key, names) {\n let f = customFields.find(x => x.fieldKey === key);\n if (!f) {\n const wanted = names.map(n => n.toLowerCase().trim());\n f = customFields.find(x => wanted.includes((x.name || \"\").toLowerCase().trim()));\n }\n return f || null;\n}\n\nconst sucursalField = findField(\"contact.sucursal\", [\"Sucursal\"]);\nconst tiendaField = findField(\"contact.tienda\", [\"TIENDA\", \"Tienda\"]);\nconst canalField = findField(\"contact.fuente_de_posible_cliente\", [\"CANAL DE ORIGEN\", \"Canal de Origen\"]);\n\n// createdBy.source SOLO viene del GET individual del contacto.\nconst contactResp = $('Obtener Contacto Cuenta Origen - SUCURSAL').item.json;\nconst createdBySource =\n (contactResp && contactResp.contact && contactResp.contact.createdBy && contactResp.contact.createdBy.source) ||\n (contactResp && contactResp.createdBy && contactResp.createdBy.source) ||\n null;\nconst esUsuario = createdBySource === \"WEB_USER\" || createdBySource === \"MOBILE_USER\";\n\nreturn [\n {\n json: {\n sucursal: {\n id: sucursalField?.id ?? null,\n name: sucursalField?.name ?? null,\n fieldKey: sucursalField?.fieldKey ?? null,\n picklistOptions: sucursalField?.picklistOptions ?? [],\n },\n tienda: {\n id: tiendaField?.id ?? null,\n name: tiendaField?.name ?? null,\n fieldKey: tiendaField?.fieldKey ?? null,\n picklistOptions: tiendaField?.picklistOptions ?? [],\n },\n canal: {\n id: canalField?.id ?? null,\n name: canalField?.name ?? null,\n fieldKey: canalField?.fieldKey ?? null,\n picklistOptions: canalField?.picklistOptions ?? [],\n },\n createdBySource: createdBySource,\n esUsuario: esUsuario,\n },\n },\n];"
|
||||
},
|
||||
"type": "n8n-nodes-base.code",
|
||||
"typeVersion": 2,
|
||||
"position": [
|
||||
32,
|
||||
-320
|
||||
],
|
||||
"id": "5a1cfe47-862c-4d28-a4ca-57a9f8c54a7c",
|
||||
"name": "Buscar \"contact.sucursal\" y \"contact.tienda\""
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"databaseId": 63,
|
||||
"tableId": 750,
|
||||
"limit": 1,
|
||||
"additionalOptions": {
|
||||
"filters": {
|
||||
"fields": [
|
||||
{
|
||||
"field": 7247,
|
||||
"value": "={{ $('Datos API Cuenta Origen - SUCURSAL').item.json['Name Location'] }}"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"type": "n8n-nodes-base.baserow",
|
||||
"typeVersion": 1.1,
|
||||
"position": [
|
||||
256,
|
||||
-320
|
||||
],
|
||||
"id": "a912fac3-25d4-492a-9648-8c472098b9ca",
|
||||
"name": "Buscar Sucursal en Verificador de Sucursales",
|
||||
"credentials": {
|
||||
"baserowApi": {
|
||||
"id": "LZztQ3WMpzXjSTIH",
|
||||
"name": "Baserow account"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"method": "POST",
|
||||
"url": "https://services.leadconnectorhq.com/opportunities/search",
|
||||
"sendHeaders": true,
|
||||
"headerParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "Accept",
|
||||
"value": "application/json"
|
||||
},
|
||||
{
|
||||
"name": "Version",
|
||||
"value": "2023-02-21"
|
||||
},
|
||||
{
|
||||
"name": "Authorization",
|
||||
"value": "=Bearer {{ $('Obtener Info de cuenta origen - SUCURSAL').item.json.API_token }}"
|
||||
}
|
||||
]
|
||||
},
|
||||
"sendBody": true,
|
||||
"specifyBody": "json",
|
||||
"jsonBody": "={\n \"locationId\": \"{{ $('Obtener Info de cuenta origen - SUCURSAL').item.json.Location_ID }}\",\n \"query\": \"{{ $json.contact.email }}\",\n \"limit\": 20,\n \"page\": 1\n}",
|
||||
"options": {
|
||||
"redirect": {
|
||||
"redirect": {}
|
||||
}
|
||||
}
|
||||
},
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"typeVersion": 4.2,
|
||||
"position": [
|
||||
1152,
|
||||
-320
|
||||
],
|
||||
"id": "4b899380-fe4a-40ad-80bb-21ef504b30ac",
|
||||
"name": "Actualizar Contacto Cuenta Objetivo - SUCURSAL1"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"conditions": {
|
||||
"options": {
|
||||
"caseSensitive": true,
|
||||
"leftValue": "",
|
||||
"typeValidation": "loose",
|
||||
"version": 3
|
||||
},
|
||||
"conditions": [
|
||||
{
|
||||
"id": "canal-origen-esusuario",
|
||||
"leftValue": "={{ $('Buscar \"contact.sucursal\" y \"contact.tienda\"').item.json.esUsuario }}",
|
||||
"rightValue": "",
|
||||
"operator": {
|
||||
"type": "boolean",
|
||||
"operation": "true",
|
||||
"singleValue": true
|
||||
}
|
||||
}
|
||||
],
|
||||
"combinator": "and"
|
||||
},
|
||||
"options": {}
|
||||
},
|
||||
"type": "n8n-nodes-base.if",
|
||||
"typeVersion": 2.3,
|
||||
"position": [
|
||||
720,
|
||||
-120
|
||||
],
|
||||
"name": "Creado por usuario (Canal de Origen)",
|
||||
"id": "1de171d0-af2b-400b-9f13-09fc5ea3526a"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"method": "PUT",
|
||||
"url": "=https://services.leadconnectorhq.com/contacts/{{ $('Datos de Lead').item.json.Cliente['Contact ID'] }}",
|
||||
"sendHeaders": true,
|
||||
"headerParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "Accept",
|
||||
"value": "application/json"
|
||||
},
|
||||
{
|
||||
"name": "Version",
|
||||
"value": "2021-07-28"
|
||||
},
|
||||
{
|
||||
"name": "Authorization",
|
||||
"value": "=Bearer {{ $('Buscar Sucursal en Verificador de Sucursales').item.json['SC TOKEN BUCEFALO'] }}"
|
||||
}
|
||||
]
|
||||
},
|
||||
"sendBody": true,
|
||||
"specifyBody": "json",
|
||||
"jsonBody": "={\n \"customFields\": [\n {\n \"id\": \"{{ $('Buscar \"contact.sucursal\" y \"contact.tienda\"').item.json.canal.id }}\",\n \"key\": \"{{ $('Buscar \"contact.sucursal\" y \"contact.tienda\"').item.json.canal.fieldKey }}\",\n \"field_value\": \"SUCURSAL\"\n }\n ]\n}",
|
||||
"options": {
|
||||
"redirect": {
|
||||
"redirect": {}
|
||||
}
|
||||
}
|
||||
},
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"typeVersion": 4.2,
|
||||
"position": [
|
||||
940,
|
||||
-120
|
||||
],
|
||||
"name": "PUT Canal de Origen = SUCURSAL",
|
||||
"id": "8e49d480-89da-4279-9529-774b141eb66e"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"method": "POST",
|
||||
"url": "=https://services.leadconnectorhq.com/contacts/{{ $('Datos de Lead').item.json.Cliente['Contact ID'] }}/tags",
|
||||
"sendHeaders": true,
|
||||
"headerParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "Accept",
|
||||
"value": "application/json"
|
||||
},
|
||||
{
|
||||
"name": "Version",
|
||||
"value": "2021-07-28"
|
||||
},
|
||||
{
|
||||
"name": "Authorization",
|
||||
"value": "=Bearer {{ $('Buscar Sucursal en Verificador de Sucursales').item.json['SC TOKEN BUCEFALO'] }}"
|
||||
}
|
||||
]
|
||||
},
|
||||
"sendBody": true,
|
||||
"specifyBody": "json",
|
||||
"jsonBody": "={\n \"tags\": [\"sucursal\"]\n}",
|
||||
"options": {
|
||||
"redirect": {
|
||||
"redirect": {}
|
||||
}
|
||||
}
|
||||
},
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"typeVersion": 4.2,
|
||||
"position": [
|
||||
1160,
|
||||
-120
|
||||
],
|
||||
"name": "Tag+ sucursal",
|
||||
"id": "a5d7de79-61f7-482a-b2a4-67f80cb1b237"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"method": "DELETE",
|
||||
"url": "=https://services.leadconnectorhq.com/contacts/{{ $('Datos de Lead').item.json.Cliente['Contact ID'] }}/tags",
|
||||
"sendHeaders": true,
|
||||
"headerParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "Accept",
|
||||
"value": "application/json"
|
||||
},
|
||||
{
|
||||
"name": "Version",
|
||||
"value": "2021-07-28"
|
||||
},
|
||||
{
|
||||
"name": "Authorization",
|
||||
"value": "=Bearer {{ $('Buscar Sucursal en Verificador de Sucursales').item.json['SC TOKEN BUCEFALO'] }}"
|
||||
}
|
||||
]
|
||||
},
|
||||
"sendBody": true,
|
||||
"specifyBody": "json",
|
||||
"jsonBody": "={\n \"tags\": [\"formulario\"]\n}",
|
||||
"options": {
|
||||
"redirect": {
|
||||
"redirect": {}
|
||||
}
|
||||
}
|
||||
},
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"typeVersion": 4.2,
|
||||
"position": [
|
||||
1380,
|
||||
-120
|
||||
],
|
||||
"name": "Tag- formulario",
|
||||
"onError": "continueRegularOutput",
|
||||
"id": "c1059239-9e91-4e9a-b757-3111689c73f7"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"method": "DELETE",
|
||||
"url": "=https://services.leadconnectorhq.com/contacts/{{ $('Datos de Lead').item.json.Cliente['Contact ID'] }}/tags",
|
||||
"sendHeaders": true,
|
||||
"headerParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "Accept",
|
||||
"value": "application/json"
|
||||
},
|
||||
{
|
||||
"name": "Version",
|
||||
"value": "2021-07-28"
|
||||
},
|
||||
{
|
||||
"name": "Authorization",
|
||||
"value": "=Bearer {{ $('Buscar Sucursal en Verificador de Sucursales').item.json['SC TOKEN BUCEFALO'] }}"
|
||||
}
|
||||
]
|
||||
},
|
||||
"sendBody": true,
|
||||
"specifyBody": "json",
|
||||
"jsonBody": "={\n \"tags\": [\"facebook-ads\"]\n}",
|
||||
"options": {
|
||||
"redirect": {
|
||||
"redirect": {}
|
||||
}
|
||||
}
|
||||
},
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"typeVersion": 4.2,
|
||||
"position": [
|
||||
1600,
|
||||
-120
|
||||
],
|
||||
"name": "Tag- facebook-ads",
|
||||
"onError": "continueRegularOutput",
|
||||
"id": "52ce0b09-1c36-43ea-a420-f4a3adb3d5f9"
|
||||
}
|
||||
],
|
||||
"connections": {
|
||||
"Obtener Info de cuenta origen - SUCURSAL": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Datos API Cuenta Origen - SUCURSAL",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Conseguir Custom Cuenta Origen- SUCURSAL": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Buscar \"contact.sucursal\" y \"contact.tienda\"",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Omitir @ezcorp.com": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Obtener Info de cuenta origen - SUCURSAL",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Datos de Lead": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Omitir @ezcorp.com",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Datos API Cuenta Origen - SUCURSAL": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Obtener Contacto Cuenta Origen - SUCURSAL",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Obtener Contacto Cuenta Origen - SUCURSAL": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Conseguir Custom Cuenta Origen- SUCURSAL",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Webhook": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Datos de Lead",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Buscar Sucursal en Verificador de Sucursales": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Actualizar Contacto Cuenta Objetivo - SUCURSAL",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Buscar \"contact.sucursal\" y \"contact.tienda\"": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Buscar Sucursal en Verificador de Sucursales",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Actualizar Contacto Cuenta Objetivo - SUCURSAL": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Creado por usuario (Canal de Origen)",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Creado por usuario (Canal de Origen)": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "PUT Canal de Origen = SUCURSAL",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"PUT Canal de Origen = SUCURSAL": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Tag+ sucursal",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Tag+ sucursal": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Tag- formulario",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Tag- formulario": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Tag- facebook-ads",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
"settings": {
|
||||
"executionOrder": "v1"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,860 @@
|
||||
{
|
||||
"name": "[2004][Monte Providencia] Actualizar \"contact.sucursal\", \"contact.tienda\" al crear contacto",
|
||||
"nodes": [
|
||||
{
|
||||
"parameters": {
|
||||
"content": "# De Sucursal a Marca",
|
||||
"color": 5
|
||||
},
|
||||
"type": "n8n-nodes-base.stickyNote",
|
||||
"position": [
|
||||
-2288,
|
||||
-320
|
||||
],
|
||||
"typeVersion": 1,
|
||||
"id": "d91325e1-1763-486c-a2c9-8cfffddd57b0",
|
||||
"name": "Sticky Note3"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"databaseId": 63,
|
||||
"tableId": 749,
|
||||
"additionalOptions": {
|
||||
"filters": {
|
||||
"fields": [
|
||||
{
|
||||
"field": 7235,
|
||||
"value": "={{ $('Datos de Lead').item.json.Sucursal['Cuenta Bucéfalo'] }}"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"type": "n8n-nodes-base.baserow",
|
||||
"typeVersion": 1,
|
||||
"position": [
|
||||
-912,
|
||||
-320
|
||||
],
|
||||
"id": "50da84c1-c2d3-4a36-91d3-e3745edccdc6",
|
||||
"name": "Obtener Info de cuenta origen - SUCURSAL",
|
||||
"credentials": {
|
||||
"baserowApi": {
|
||||
"id": "LZztQ3WMpzXjSTIH",
|
||||
"name": "Baserow account"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"url": "=https://services.leadconnectorhq.com/locations/{{ $('Datos API Cuenta Origen - SUCURSAL').item.json['Location ID'] }}/customFields",
|
||||
"sendHeaders": true,
|
||||
"headerParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "Accept",
|
||||
"value": "application/json"
|
||||
},
|
||||
{
|
||||
"name": "Version",
|
||||
"value": "2021-07-28"
|
||||
},
|
||||
{
|
||||
"name": "Authorization",
|
||||
"value": "=Bearer {{ $('Datos API Cuenta Origen - SUCURSAL').item.json['Token/API'] }}"
|
||||
}
|
||||
]
|
||||
},
|
||||
"options": {
|
||||
"redirect": {
|
||||
"redirect": {}
|
||||
}
|
||||
}
|
||||
},
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"typeVersion": 4.2,
|
||||
"position": [
|
||||
-224,
|
||||
-320
|
||||
],
|
||||
"id": "a0c27c4c-0f20-4bab-bf6b-bcdd8281a92b",
|
||||
"name": "Conseguir Custom Cuenta Origen- SUCURSAL"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"conditions": {
|
||||
"options": {
|
||||
"caseSensitive": true,
|
||||
"leftValue": "",
|
||||
"typeValidation": "strict",
|
||||
"version": 3
|
||||
},
|
||||
"conditions": [
|
||||
{
|
||||
"id": "44d54b9e-d192-4b54-bf0c-156b79afc6e2",
|
||||
"leftValue": "={{ $json.Cliente.Email }}",
|
||||
"rightValue": "@ezcorp.com",
|
||||
"operator": {
|
||||
"type": "string",
|
||||
"operation": "notContains"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "64f2add6-506c-4950-8026-c04c9547aeeb",
|
||||
"leftValue": "",
|
||||
"rightValue": "",
|
||||
"operator": {
|
||||
"type": "string",
|
||||
"operation": "equals",
|
||||
"name": "filter.operator.equals"
|
||||
}
|
||||
}
|
||||
],
|
||||
"combinator": "and"
|
||||
},
|
||||
"options": {}
|
||||
},
|
||||
"type": "n8n-nodes-base.if",
|
||||
"typeVersion": 2.3,
|
||||
"position": [
|
||||
-1392,
|
||||
-304
|
||||
],
|
||||
"id": "d68558b5-52b8-46b1-a359-fd956c7edc09",
|
||||
"name": "Omitir @ezcorp.com"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"assignments": {
|
||||
"assignments": [
|
||||
{
|
||||
"id": "8a998fd4-2de6-4895-ab3d-e052e823d1b8",
|
||||
"name": "Cliente.Fuente Posible Cliente",
|
||||
"value": "={{ $json.body['Fuente de Posible cliente'] }}",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"id": "938c6fec-ae16-4e7a-ba2a-f450794fa40d",
|
||||
"name": "Cliente.Fecha de creación",
|
||||
"value": "={{ $json.body.date_created }}",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"id": "b56a1939-2608-47c8-85ad-b60b557d2a27",
|
||||
"name": "Cliente.Sucursal.Sucursal",
|
||||
"value": "={{ $json.body.Sucursal }}",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"id": "0d07b9c9-4450-497b-ab81-3baa441787fb",
|
||||
"name": "Vehiculo.Versión.Versión",
|
||||
"value": "={{ $json.body['Version del Vehiculo'] }}",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"id": "75e3f337-00d1-429d-8d5b-85ec18e6c5e4",
|
||||
"name": "Vehiculo.Marca.Marca",
|
||||
"value": "={{ $json.body['Marca del Vehiculo'] }}",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"id": "cb09c536-fe84-4598-aaae-aa79f2eda61b",
|
||||
"name": "Vehiculo.Marca.fieldKey",
|
||||
"value": "=contact.marca_del_vehiculo",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"id": "17d36409-4c54-48dd-8100-f7f667fd2415",
|
||||
"name": "Vehiculo.Año.Año",
|
||||
"value": "={{ $json.body['Año del Vehículo'] }}",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"id": "a1886afc-b0af-4950-9752-f8bfff594896",
|
||||
"name": "Vehiculo.Modalidad.Modalidad",
|
||||
"value": "={{ $json.body['¿Qué modalidad prefieres?'] }}",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"id": "33b2c28a-1ad3-4c74-917f-3cd718a3a709",
|
||||
"name": "Cliente.Nombre",
|
||||
"value": "={{ $json.body['Información Adicional'] }}",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"id": "b36131ac-2d88-41f8-8f0f-7640cdb02b57",
|
||||
"name": "Cliente.Apellido",
|
||||
"value": "={{ $json.body.first_name }}",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"id": "ae252c8f-f0a1-41d9-a21d-04ca949f01c8",
|
||||
"name": "Cliente.Nombre Completo",
|
||||
"value": "={{ $json.body.full_name }}",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"id": "342a9377-0ded-4f23-b93b-1f76e57c0cbd",
|
||||
"name": "Cliente.Email",
|
||||
"value": "={{ $json.body.email }}",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"id": "cf1b7058-96c2-4c73-9250-719a88b68673",
|
||||
"name": "Cliente.Telefono",
|
||||
"value": "={{ $json.body.phone }}",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"id": "0b916193-15e8-4a91-9ff2-0a6f262b3c38",
|
||||
"name": "Cliente.Contact ID",
|
||||
"value": "={{ $json.body.contact_id }}",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"id": "67eeaa9b-f703-4521-82af-9d2797131edc",
|
||||
"name": "Cliente.Sucursal.fieldKey",
|
||||
"value": "contact.sucursal",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"id": "b05ea7bb-bbaa-467b-8247-eabb162ff029",
|
||||
"name": "Vehiculo.Versión.fieldKey",
|
||||
"value": "contact.version_del_vehiculo",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"id": "9891b919-ef4c-46bd-8414-6d916565d896",
|
||||
"name": "Vehiculo.Año.fieldKey",
|
||||
"value": "contact.ano_del_vehiculo",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"id": "0b6bf582-49c1-41de-9c2e-6ae85c4e41e8",
|
||||
"name": "Vehiculo.Modalidad.fieldKey",
|
||||
"value": "contact.que_modalidad_prefieres",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"id": "57199999-2d9b-41c7-8269-5a8183ca8132",
|
||||
"name": "Cliente.Cuándo necesitas el dinero",
|
||||
"value": "={{ $json.body[\"¿Cuándo necesitas el dinero?\"] }}",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"id": "8060d06e-b1da-4a65-8a0a-3c21d237d77e",
|
||||
"name": "Sucursal.Cuenta Bucéfalo",
|
||||
"value": "={{ $json.body.location.name }}",
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"options": {}
|
||||
},
|
||||
"type": "n8n-nodes-base.set",
|
||||
"typeVersion": 3.4,
|
||||
"position": [
|
||||
-1568,
|
||||
-304
|
||||
],
|
||||
"id": "d0e455ac-7ccc-4c55-bda2-1cd22092e2d2",
|
||||
"name": "Datos de Lead"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"assignments": {
|
||||
"assignments": [
|
||||
{
|
||||
"id": "55ff7d12-17b9-4bec-a324-e633020b131d",
|
||||
"name": "Name Location",
|
||||
"value": "={{ $('Obtener Info de cuenta origen - SUCURSAL').item.json.Nombre }}",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"id": "d877c8cd-db32-4c16-96dd-4eeb2dc48efe",
|
||||
"name": "Location ID",
|
||||
"value": "={{ $('Obtener Info de cuenta origen - SUCURSAL').item.json.Location_ID }}",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"id": "7698f395-5db8-415b-919e-3ad61c6566f8",
|
||||
"name": "Token/API",
|
||||
"value": "={{ $('Obtener Info de cuenta origen - SUCURSAL').item.json.API_token }}",
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"options": {}
|
||||
},
|
||||
"type": "n8n-nodes-base.set",
|
||||
"typeVersion": 3.4,
|
||||
"position": [
|
||||
-720,
|
||||
-320
|
||||
],
|
||||
"id": "f0cff3ec-8464-45d4-9e64-713c36e247c6",
|
||||
"name": "Datos API Cuenta Origen - SUCURSAL",
|
||||
"notes": "Esta en modo prueba forzada para Queretaro"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"content": "# CUENTA ORIGEN",
|
||||
"height": 240,
|
||||
"width": 1744
|
||||
},
|
||||
"type": "n8n-nodes-base.stickyNote",
|
||||
"position": [
|
||||
-976,
|
||||
-384
|
||||
],
|
||||
"typeVersion": 1,
|
||||
"id": "16a74d85-1e6c-4fc7-b854-580a2d3827a0",
|
||||
"name": "Sticky Note"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"url": "=https://services.leadconnectorhq.com/contacts/{{ $('Webhook').item.json.body.contact_id }}",
|
||||
"sendHeaders": true,
|
||||
"headerParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "Accept",
|
||||
"value": "application/json"
|
||||
},
|
||||
{
|
||||
"name": "Version",
|
||||
"value": "2021-07-28"
|
||||
},
|
||||
{
|
||||
"name": "Authorization",
|
||||
"value": "=Bearer {{ $json['Token/API'] }}"
|
||||
}
|
||||
]
|
||||
},
|
||||
"options": {
|
||||
"redirect": {
|
||||
"redirect": {}
|
||||
}
|
||||
}
|
||||
},
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"typeVersion": 4.4,
|
||||
"position": [
|
||||
-400,
|
||||
-320
|
||||
],
|
||||
"id": "92f33c6e-ee64-409d-8c60-9fbfe48b3265",
|
||||
"name": "Obtener Contacto Cuenta Origen - SUCURSAL"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"content": "",
|
||||
"height": 176,
|
||||
"width": 608,
|
||||
"color": 7
|
||||
},
|
||||
"type": "n8n-nodes-base.stickyNote",
|
||||
"position": [
|
||||
-448,
|
||||
-336
|
||||
],
|
||||
"typeVersion": 1,
|
||||
"id": "768a4001-5109-493f-b12e-e8ba5d30ec2f",
|
||||
"name": "Sticky Note1"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"method": "PUT",
|
||||
"url": "=https://services.leadconnectorhq.com/contacts/{{ $('Datos de Lead').item.json.Cliente['Contact ID'] }}",
|
||||
"sendHeaders": true,
|
||||
"headerParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "Accept",
|
||||
"value": "application/json"
|
||||
},
|
||||
{
|
||||
"name": "Version",
|
||||
"value": "2021-07-28"
|
||||
},
|
||||
{
|
||||
"name": "Authorization",
|
||||
"value": "=Bearer {{ $json['SC TOKEN BUCEFALO'] }}"
|
||||
}
|
||||
]
|
||||
},
|
||||
"sendBody": true,
|
||||
"specifyBody": "json",
|
||||
"jsonBody": "={\n \"customFields\": [\n {\n \"id\": \"{{ $('Buscar \"contact.sucursal\" y \"contact.tienda\"').item.json.sucursal.id }}\",\n \"key\": \"contact.sucursal\",\n \"field_value\": \"{{ $json.SUCURSAL }}\"\n },\n {\n \"id\": \"{{ $('Buscar \"contact.sucursal\" y \"contact.tienda\"').item.json.tienda.id }}\",\n \"key\": \"contact.tienda\",\n \"field_value\": \"{{ $json.TIENDA }}\"\n }\n ]\n}",
|
||||
"options": {
|
||||
"redirect": {
|
||||
"redirect": {}
|
||||
}
|
||||
}
|
||||
},
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"typeVersion": 4.2,
|
||||
"position": [
|
||||
512,
|
||||
-320
|
||||
],
|
||||
"id": "794ce5bb-dea0-4720-958a-1f0940c79e6d",
|
||||
"name": "Actualizar Contacto Cuenta Objetivo - SUCURSAL"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"httpMethod": "POST",
|
||||
"path": "8d574598-d977-4052-823a-26def39c6a64",
|
||||
"options": {}
|
||||
},
|
||||
"type": "n8n-nodes-base.webhook",
|
||||
"typeVersion": 2.1,
|
||||
"position": [
|
||||
-1776,
|
||||
-304
|
||||
],
|
||||
"id": "d4312610-e978-424c-a8a0-426026c4d4f6",
|
||||
"name": "Webhook",
|
||||
"webhookId": "8d574598-d977-4052-823a-26def39c6a64"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"jsCode": "const customFields = $input.first().json.customFields;\n\nfunction findField(key, names) {\n let f = customFields.find(x => x.fieldKey === key);\n if (!f) {\n const wanted = names.map(n => n.toLowerCase().trim());\n f = customFields.find(x => wanted.includes((x.name || \"\").toLowerCase().trim()));\n }\n return f || null;\n}\n\nconst sucursalField = findField(\"contact.sucursal\", [\"Sucursal\"]);\nconst tiendaField = findField(\"contact.tienda\", [\"TIENDA\", \"Tienda\"]);\nconst canalField = findField(\"contact.fuente_de_posible_cliente\", [\"CANAL DE ORIGEN\", \"Canal de Origen\"]);\n\n// createdBy.source SOLO viene del GET individual del contacto.\nconst contactResp = $('Obtener Contacto Cuenta Origen - SUCURSAL').item.json;\nconst createdBySource =\n (contactResp && contactResp.contact && contactResp.contact.createdBy && contactResp.contact.createdBy.source) ||\n (contactResp && contactResp.createdBy && contactResp.createdBy.source) ||\n null;\nconst esUsuario = createdBySource === \"WEB_USER\" || createdBySource === \"MOBILE_USER\";\n\nreturn [\n {\n json: {\n sucursal: {\n id: sucursalField?.id ?? null,\n name: sucursalField?.name ?? null,\n fieldKey: sucursalField?.fieldKey ?? null,\n picklistOptions: sucursalField?.picklistOptions ?? [],\n },\n tienda: {\n id: tiendaField?.id ?? null,\n name: tiendaField?.name ?? null,\n fieldKey: tiendaField?.fieldKey ?? null,\n picklistOptions: tiendaField?.picklistOptions ?? [],\n },\n canal: {\n id: canalField?.id ?? null,\n name: canalField?.name ?? null,\n fieldKey: canalField?.fieldKey ?? null,\n picklistOptions: canalField?.picklistOptions ?? [],\n },\n createdBySource: createdBySource,\n esUsuario: esUsuario,\n },\n },\n];"
|
||||
},
|
||||
"type": "n8n-nodes-base.code",
|
||||
"typeVersion": 2,
|
||||
"position": [
|
||||
32,
|
||||
-320
|
||||
],
|
||||
"id": "5a1cfe47-862c-4d28-a4ca-57a9f8c54a7c",
|
||||
"name": "Buscar \"contact.sucursal\" y \"contact.tienda\""
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"databaseId": 63,
|
||||
"tableId": 750,
|
||||
"limit": 1,
|
||||
"additionalOptions": {
|
||||
"filters": {
|
||||
"fields": [
|
||||
{
|
||||
"field": 7247,
|
||||
"value": "={{ $('Datos API Cuenta Origen - SUCURSAL').item.json['Name Location'] }}"
|
||||
},
|
||||
{
|
||||
"field": 7279,
|
||||
"operator": "not_equal",
|
||||
"value": "NO DIGITAL"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"type": "n8n-nodes-base.baserow",
|
||||
"typeVersion": 1.1,
|
||||
"position": [
|
||||
256,
|
||||
-320
|
||||
],
|
||||
"id": "a912fac3-25d4-492a-9648-8c472098b9ca",
|
||||
"name": "Buscar Sucursal en Verificador de Sucursales",
|
||||
"credentials": {
|
||||
"baserowApi": {
|
||||
"id": "LZztQ3WMpzXjSTIH",
|
||||
"name": "Baserow account"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"method": "POST",
|
||||
"url": "https://services.leadconnectorhq.com/opportunities/search",
|
||||
"sendHeaders": true,
|
||||
"headerParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "Accept",
|
||||
"value": "application/json"
|
||||
},
|
||||
{
|
||||
"name": "Version",
|
||||
"value": "2023-02-21"
|
||||
},
|
||||
{
|
||||
"name": "Authorization",
|
||||
"value": "=Bearer {{ $('Obtener Info de cuenta origen - SUCURSAL').item.json.API_token }}"
|
||||
}
|
||||
]
|
||||
},
|
||||
"sendBody": true,
|
||||
"specifyBody": "json",
|
||||
"jsonBody": "={\n \"locationId\": \"{{ $('Obtener Info de cuenta origen - SUCURSAL').item.json.Location_ID }}\",\n \"query\": \"{{ $json.contact.email }}\",\n \"limit\": 20,\n \"page\": 1\n}",
|
||||
"options": {
|
||||
"redirect": {
|
||||
"redirect": {}
|
||||
}
|
||||
}
|
||||
},
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"typeVersion": 4.2,
|
||||
"position": [
|
||||
1152,
|
||||
-320
|
||||
],
|
||||
"id": "4b899380-fe4a-40ad-80bb-21ef504b30ac",
|
||||
"name": "Actualizar Contacto Cuenta Objetivo - SUCURSAL1"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"conditions": {
|
||||
"options": {
|
||||
"caseSensitive": true,
|
||||
"leftValue": "",
|
||||
"typeValidation": "loose",
|
||||
"version": 3
|
||||
},
|
||||
"conditions": [
|
||||
{
|
||||
"id": "canal-origen-esusuario",
|
||||
"leftValue": "={{ $('Buscar \"contact.sucursal\" y \"contact.tienda\"').item.json.esUsuario }}",
|
||||
"rightValue": "",
|
||||
"operator": {
|
||||
"type": "boolean",
|
||||
"operation": "true",
|
||||
"singleValue": true
|
||||
}
|
||||
}
|
||||
],
|
||||
"combinator": "and"
|
||||
},
|
||||
"options": {}
|
||||
},
|
||||
"type": "n8n-nodes-base.if",
|
||||
"typeVersion": 2.3,
|
||||
"position": [
|
||||
720,
|
||||
-120
|
||||
],
|
||||
"name": "Creado por usuario (Canal de Origen)",
|
||||
"id": "49879f5e-7ce8-4ced-b1a6-96df44ac2e0a"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"method": "PUT",
|
||||
"url": "=https://services.leadconnectorhq.com/contacts/{{ $('Datos de Lead').item.json.Cliente['Contact ID'] }}",
|
||||
"sendHeaders": true,
|
||||
"headerParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "Accept",
|
||||
"value": "application/json"
|
||||
},
|
||||
{
|
||||
"name": "Version",
|
||||
"value": "2021-07-28"
|
||||
},
|
||||
{
|
||||
"name": "Authorization",
|
||||
"value": "=Bearer {{ $('Buscar Sucursal en Verificador de Sucursales').item.json['SC TOKEN BUCEFALO'] }}"
|
||||
}
|
||||
]
|
||||
},
|
||||
"sendBody": true,
|
||||
"specifyBody": "json",
|
||||
"jsonBody": "={\n \"customFields\": [\n {\n \"id\": \"{{ $('Buscar \"contact.sucursal\" y \"contact.tienda\"').item.json.canal.id }}\",\n \"key\": \"{{ $('Buscar \"contact.sucursal\" y \"contact.tienda\"').item.json.canal.fieldKey }}\",\n \"field_value\": \"SUCURSAL\"\n }\n ]\n}",
|
||||
"options": {
|
||||
"redirect": {
|
||||
"redirect": {}
|
||||
}
|
||||
}
|
||||
},
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"typeVersion": 4.2,
|
||||
"position": [
|
||||
940,
|
||||
-120
|
||||
],
|
||||
"name": "PUT Canal de Origen = SUCURSAL",
|
||||
"id": "d0597a9c-aca0-40bb-98d8-bb584d2a2c3e"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"method": "POST",
|
||||
"url": "=https://services.leadconnectorhq.com/contacts/{{ $('Datos de Lead').item.json.Cliente['Contact ID'] }}/tags",
|
||||
"sendHeaders": true,
|
||||
"headerParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "Accept",
|
||||
"value": "application/json"
|
||||
},
|
||||
{
|
||||
"name": "Version",
|
||||
"value": "2021-07-28"
|
||||
},
|
||||
{
|
||||
"name": "Authorization",
|
||||
"value": "=Bearer {{ $('Buscar Sucursal en Verificador de Sucursales').item.json['SC TOKEN BUCEFALO'] }}"
|
||||
}
|
||||
]
|
||||
},
|
||||
"sendBody": true,
|
||||
"specifyBody": "json",
|
||||
"jsonBody": "={\n \"tags\": [\"sucursal\"]\n}",
|
||||
"options": {
|
||||
"redirect": {
|
||||
"redirect": {}
|
||||
}
|
||||
}
|
||||
},
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"typeVersion": 4.2,
|
||||
"position": [
|
||||
1160,
|
||||
-120
|
||||
],
|
||||
"name": "Tag+ sucursal",
|
||||
"id": "86bfddcb-c402-413f-9d32-c55050dc470d"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"method": "DELETE",
|
||||
"url": "=https://services.leadconnectorhq.com/contacts/{{ $('Datos de Lead').item.json.Cliente['Contact ID'] }}/tags",
|
||||
"sendHeaders": true,
|
||||
"headerParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "Accept",
|
||||
"value": "application/json"
|
||||
},
|
||||
{
|
||||
"name": "Version",
|
||||
"value": "2021-07-28"
|
||||
},
|
||||
{
|
||||
"name": "Authorization",
|
||||
"value": "=Bearer {{ $('Buscar Sucursal en Verificador de Sucursales').item.json['SC TOKEN BUCEFALO'] }}"
|
||||
}
|
||||
]
|
||||
},
|
||||
"sendBody": true,
|
||||
"specifyBody": "json",
|
||||
"jsonBody": "={\n \"tags\": [\"formulario\"]\n}",
|
||||
"options": {
|
||||
"redirect": {
|
||||
"redirect": {}
|
||||
}
|
||||
}
|
||||
},
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"typeVersion": 4.2,
|
||||
"position": [
|
||||
1380,
|
||||
-120
|
||||
],
|
||||
"name": "Tag- formulario",
|
||||
"onError": "continueRegularOutput",
|
||||
"id": "cbd3cfb2-28d0-44e1-9567-a269382497ae"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"method": "DELETE",
|
||||
"url": "=https://services.leadconnectorhq.com/contacts/{{ $('Datos de Lead').item.json.Cliente['Contact ID'] }}/tags",
|
||||
"sendHeaders": true,
|
||||
"headerParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "Accept",
|
||||
"value": "application/json"
|
||||
},
|
||||
{
|
||||
"name": "Version",
|
||||
"value": "2021-07-28"
|
||||
},
|
||||
{
|
||||
"name": "Authorization",
|
||||
"value": "=Bearer {{ $('Buscar Sucursal en Verificador de Sucursales').item.json['SC TOKEN BUCEFALO'] }}"
|
||||
}
|
||||
]
|
||||
},
|
||||
"sendBody": true,
|
||||
"specifyBody": "json",
|
||||
"jsonBody": "={\n \"tags\": [\"facebook-ads\"]\n}",
|
||||
"options": {
|
||||
"redirect": {
|
||||
"redirect": {}
|
||||
}
|
||||
}
|
||||
},
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"typeVersion": 4.2,
|
||||
"position": [
|
||||
1600,
|
||||
-120
|
||||
],
|
||||
"name": "Tag- facebook-ads",
|
||||
"onError": "continueRegularOutput",
|
||||
"id": "19bdc2b2-9345-4294-8e59-7f35963f261d"
|
||||
}
|
||||
],
|
||||
"connections": {
|
||||
"Obtener Info de cuenta origen - SUCURSAL": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Datos API Cuenta Origen - SUCURSAL",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Conseguir Custom Cuenta Origen- SUCURSAL": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Buscar \"contact.sucursal\" y \"contact.tienda\"",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Omitir @ezcorp.com": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Obtener Info de cuenta origen - SUCURSAL",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Datos de Lead": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Omitir @ezcorp.com",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Datos API Cuenta Origen - SUCURSAL": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Obtener Contacto Cuenta Origen - SUCURSAL",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Obtener Contacto Cuenta Origen - SUCURSAL": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Conseguir Custom Cuenta Origen- SUCURSAL",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Webhook": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Datos de Lead",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Buscar Sucursal en Verificador de Sucursales": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Actualizar Contacto Cuenta Objetivo - SUCURSAL",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Buscar \"contact.sucursal\" y \"contact.tienda\"": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Buscar Sucursal en Verificador de Sucursales",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Actualizar Contacto Cuenta Objetivo - SUCURSAL": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Creado por usuario (Canal de Origen)",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Creado por usuario (Canal de Origen)": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "PUT Canal de Origen = SUCURSAL",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"PUT Canal de Origen = SUCURSAL": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Tag+ sucursal",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Tag+ sucursal": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Tag- formulario",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Tag- formulario": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Tag- facebook-ads",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
"settings": {
|
||||
"executionOrder": "v1"
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user