@graph8/sdk 0.308.1 → 0.357.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +81 -0
- package/blocks/README.md +32 -0
- package/blocks/_foundation/README.md +78 -0
- package/blocks/_foundation/block.json +79 -0
- package/blocks/_foundation/components/api-error.tsx +62 -0
- package/blocks/_foundation/components/confirm-spend-dialog.tsx +142 -0
- package/blocks/_foundation/components/empty-state.tsx +24 -0
- package/blocks/_foundation/hooks/use-g8-mutation.ts +88 -0
- package/blocks/_foundation/hooks/use-g8-query.ts +87 -0
- package/blocks/_foundation/hooks/use-job-poll.ts +130 -0
- package/blocks/_foundation/hooks/use-paginated.ts +105 -0
- package/blocks/_foundation/lib/g8-client.ts +128 -0
- package/blocks/_foundation/recipes/verify-proxy-key.ts +18 -0
- package/blocks/_foundation/server/proxy.ts +233 -0
- package/blocks/_foundation/server/route.ts +30 -0
- package/blocks/agents/README.md +133 -0
- package/blocks/agents/block.json +223 -0
- package/blocks/agents/components/agent-builder.tsx +199 -0
- package/blocks/agents/components/agent-knowledge-panel.tsx +144 -0
- package/blocks/agents/components/agent-runs.tsx +175 -0
- package/blocks/agents/components/agent-skills-panel.tsx +126 -0
- package/blocks/agents/components/agent-test-console.tsx +175 -0
- package/blocks/agents/lib/agent-types.ts +114 -0
- package/blocks/agents/recipes/create-agent-and-attach-skill.ts +50 -0
- package/blocks/agents/recipes/run-and-follow-agent-run.ts +46 -0
- package/blocks/agents/recipes/test-agent-before-activating.ts +36 -0
- package/blocks/analytics/README.md +73 -0
- package/blocks/analytics/block.json +178 -0
- package/blocks/analytics/components/funnel-chart.tsx +79 -0
- package/blocks/analytics/components/kpi-tiles.tsx +74 -0
- package/blocks/analytics/components/leaderboard.tsx +86 -0
- package/blocks/analytics/components/report-builder.tsx +200 -0
- package/blocks/analytics/hooks/use-report.ts +26 -0
- package/blocks/analytics/lib/analytics-types.ts +108 -0
- package/blocks/analytics/lib/use-debounced.ts +16 -0
- package/blocks/analytics/recipes/01-weekly-outbound-report.ts +32 -0
- package/blocks/analytics/recipes/02-revenue-snapshot.ts +22 -0
- package/blocks/analytics/recipes/03-saved-report.ts +30 -0
- package/blocks/appointments/README.md +80 -0
- package/blocks/appointments/block.json +218 -0
- package/blocks/appointments/components/availability-editor.tsx +192 -0
- package/blocks/appointments/components/booking-page.tsx +154 -0
- package/blocks/appointments/components/event-type-editor.tsx +214 -0
- package/blocks/appointments/components/meeting-detail.tsx +147 -0
- package/blocks/appointments/components/meeting-list.tsx +148 -0
- package/blocks/appointments/hooks/use-available-slots.ts +29 -0
- package/blocks/appointments/hooks/use-schedules.ts +18 -0
- package/blocks/appointments/lib/appointments-types.ts +159 -0
- package/blocks/appointments/lib/idempotency.ts +30 -0
- package/blocks/appointments/recipes/01-create-event-type-and-hours.ts +27 -0
- package/blocks/appointments/recipes/02-find-slots-and-book.ts +34 -0
- package/blocks/appointments/recipes/03-bookings-digest.ts +22 -0
- package/blocks/appointments/recipes/04-calendar-invite.ts +21 -0
- package/blocks/block.schema.json +130 -0
- package/blocks/block.types.ts +109 -0
- package/blocks/campaigns/README.md +118 -0
- package/blocks/campaigns/block.json +361 -0
- package/blocks/campaigns/components/campaign-assets.tsx +180 -0
- package/blocks/campaigns/components/campaign-audience-step.tsx +91 -0
- package/blocks/campaigns/components/campaign-launch-step.tsx +147 -0
- package/blocks/campaigns/components/campaign-list.tsx +120 -0
- package/blocks/campaigns/components/campaign-metrics.tsx +93 -0
- package/blocks/campaigns/components/campaign-select.tsx +52 -0
- package/blocks/campaigns/components/campaign-sequence-step.tsx +158 -0
- package/blocks/campaigns/components/campaign-wizard.tsx +238 -0
- package/blocks/campaigns/components/form-field.tsx +29 -0
- package/blocks/campaigns/components/icp-editor.tsx +230 -0
- package/blocks/campaigns/components/persona-editor.tsx +210 -0
- package/blocks/campaigns/hooks/use-company-profile.ts +34 -0
- package/blocks/campaigns/lib/campaigns-utils.ts +155 -0
- package/blocks/campaigns/recipes/attach-audience-and-launch.ts +33 -0
- package/blocks/campaigns/recipes/campaign-report.ts +22 -0
- package/blocks/campaigns/recipes/generate-ideas-and-create-campaign.ts +51 -0
- package/blocks/campaigns/recipes/set-up-icp-and-personas.ts +37 -0
- package/blocks/coach/README.md +93 -0
- package/blocks/coach/block.json +125 -0
- package/blocks/coach/components/call-scorecard.tsx +126 -0
- package/blocks/coach/components/coaching-feed.tsx +140 -0
- package/blocks/coach/lib/coach-types.ts +116 -0
- package/blocks/coach/recipes/read-a-scorecard.ts +27 -0
- package/blocks/coach/recipes/weekly-coaching-digest.ts +53 -0
- package/blocks/copilot/README.md +99 -0
- package/blocks/copilot/block.json +151 -0
- package/blocks/copilot/components/copilot-memory.tsx +87 -0
- package/blocks/copilot/components/copilot-panel.tsx +189 -0
- package/blocks/copilot/components/copilot-team-chat.tsx +111 -0
- package/blocks/copilot/lib/copilot-types.ts +111 -0
- package/blocks/copilot/recipes/ask-the-copilot.ts +35 -0
- package/blocks/copilot/recipes/post-in-a-work-channel.ts +33 -0
- package/blocks/copilot/recipes/remember-and-recall.ts +19 -0
- package/blocks/crm/README.md +122 -0
- package/blocks/crm/block.json +378 -0
- package/blocks/crm/components/activity-timeline.tsx +99 -0
- package/blocks/crm/components/company-detail.tsx +118 -0
- package/blocks/crm/components/company-table.tsx +173 -0
- package/blocks/crm/components/contact-detail.tsx +135 -0
- package/blocks/crm/components/contact-table.tsx +217 -0
- package/blocks/crm/components/dedupe-review.tsx +164 -0
- package/blocks/crm/components/import-wizard.tsx +193 -0
- package/blocks/crm/components/notes-panel.tsx +112 -0
- package/blocks/crm/components/record-form.tsx +191 -0
- package/blocks/crm/components/task-list.tsx +107 -0
- package/blocks/crm/hooks/use-custom-fields.ts +18 -0
- package/blocks/crm/hooks/use-debounced.ts +13 -0
- package/blocks/crm/hooks/use-page-query.ts +52 -0
- package/blocks/crm/lib/crm-types.ts +136 -0
- package/blocks/crm/recipes/custom-fields-and-values.ts +28 -0
- package/blocks/crm/recipes/dedupe-and-merge.ts +30 -0
- package/blocks/crm/recipes/notes-tasks-and-activity.ts +23 -0
- package/blocks/crm/recipes/upsert-contact-with-company.ts +33 -0
- package/blocks/deals/README.md +78 -0
- package/blocks/deals/block.json +198 -0
- package/blocks/deals/components/deal-detail.tsx +134 -0
- package/blocks/deals/components/forecast-view.tsx +112 -0
- package/blocks/deals/components/pipeline-board.tsx +142 -0
- package/blocks/deals/components/stage-editor.tsx +96 -0
- package/blocks/deals/hooks/use-pipeline-deals.ts +67 -0
- package/blocks/deals/hooks/use-pipelines.ts +19 -0
- package/blocks/deals/lib/deals-types.ts +128 -0
- package/blocks/deals/recipes/01-create-deal-and-advance.ts +32 -0
- package/blocks/deals/recipes/02-pipeline-report.ts +35 -0
- package/blocks/deals/recipes/03-stale-deals-sweep.ts +20 -0
- package/blocks/dialer/README.md +114 -0
- package/blocks/dialer/block.json +260 -0
- package/blocks/dialer/components/call-log.tsx +78 -0
- package/blocks/dialer/components/call-queue.tsx +188 -0
- package/blocks/dialer/components/dialer-workspace.tsx +47 -0
- package/blocks/dialer/components/disposition-panel.tsx +75 -0
- package/blocks/dialer/components/live-calls-table.tsx +107 -0
- package/blocks/dialer/components/session-start-form.tsx +111 -0
- package/blocks/dialer/components/softphone.tsx +122 -0
- package/blocks/dialer/components/transcript-viewer.tsx +82 -0
- package/blocks/dialer/hooks/use-dialer-numbers.ts +19 -0
- package/blocks/dialer/hooks/use-dialer-session.ts +68 -0
- package/blocks/dialer/hooks/use-dispositions.ts +29 -0
- package/blocks/dialer/lib/dialer-types.ts +158 -0
- package/blocks/dialer/lib/softphone-transport.ts +67 -0
- package/blocks/dialer/lib/twilio-transport.ts +74 -0
- package/blocks/dialer/recipes/01-create-session-and-dial.ts +39 -0
- package/blocks/dialer/recipes/02-disposition-calls.ts +26 -0
- package/blocks/dialer/recipes/03-review-a-call.ts +23 -0
- package/blocks/enrichment/README.md +105 -0
- package/blocks/enrichment/block.json +204 -0
- package/blocks/enrichment/components/bulk-enrich-panel.tsx +205 -0
- package/blocks/enrichment/components/email-verify-badge.tsx +82 -0
- package/blocks/enrichment/components/enrich-button.tsx +136 -0
- package/blocks/enrichment/components/waterfall-config.tsx +127 -0
- package/blocks/enrichment/hooks/use-providers.ts +18 -0
- package/blocks/enrichment/lib/enrichment-types.ts +104 -0
- package/blocks/enrichment/recipes/bulk-enrich-and-poll.ts +41 -0
- package/blocks/enrichment/recipes/lookup-and-verify.ts +25 -0
- package/blocks/enrichment/recipes/waterfall-pipeline.ts +26 -0
- package/blocks/forms/README.md +112 -0
- package/blocks/forms/block.json +200 -0
- package/blocks/forms/components/form-builder.tsx +150 -0
- package/blocks/forms/components/form-embed.tsx +103 -0
- package/blocks/forms/components/form-mapping-editor.tsx +125 -0
- package/blocks/forms/components/site-audit-panel.tsx +73 -0
- package/blocks/forms/components/submissions-table.tsx +158 -0
- package/blocks/forms/lib/form-types.ts +127 -0
- package/blocks/forms/recipes/install-snippet-and-form.ts +21 -0
- package/blocks/forms/recipes/retry-failed-deliveries.ts +26 -0
- package/blocks/forms/recipes/route-form-to-list.ts +44 -0
- package/blocks/inbox/README.md +72 -0
- package/blocks/inbox/block.json +194 -0
- package/blocks/inbox/components/inbox-list.tsx +156 -0
- package/blocks/inbox/components/inbox-screen.tsx +49 -0
- package/blocks/inbox/components/reply-composer.tsx +183 -0
- package/blocks/inbox/components/tag-and-assign.tsx +189 -0
- package/blocks/inbox/components/thread-badge.tsx +17 -0
- package/blocks/inbox/components/thread-view.tsx +108 -0
- package/blocks/inbox/hooks/use-inbox-thread.ts +24 -0
- package/blocks/inbox/hooks/use-inbox-threads.ts +40 -0
- package/blocks/inbox/lib/idempotency.ts +30 -0
- package/blocks/inbox/lib/inbox-types.ts +100 -0
- package/blocks/inbox/recipes/01-triage-and-reply.ts +43 -0
- package/blocks/inbox/recipes/02-tag-and-assign.ts +31 -0
- package/blocks/inbox/recipes/03-hot-contacts-digest.ts +16 -0
- package/blocks/integrations/README.md +75 -0
- package/blocks/integrations/block.json +172 -0
- package/blocks/integrations/components/integration-list.tsx +165 -0
- package/blocks/integrations/components/sync-status.tsx +223 -0
- package/blocks/integrations/components/webhook-manager.tsx +300 -0
- package/blocks/integrations/lib/integrations-utils.ts +62 -0
- package/blocks/integrations/recipes/crm-sync-health.ts +19 -0
- package/blocks/integrations/recipes/push-contacts-to-crm.ts +20 -0
- package/blocks/integrations/recipes/register-webhook.ts +18 -0
- package/blocks/integrations/recipes/verify-webhook-route.ts +33 -0
- package/blocks/intent/README.md +57 -0
- package/blocks/intent/block.json +138 -0
- package/blocks/intent/components/account-intent-score.tsx +91 -0
- package/blocks/intent/components/intent-feed.tsx +215 -0
- package/blocks/intent/components/intent-keyword-manager.tsx +331 -0
- package/blocks/intent/hooks/use-account-intent.ts +46 -0
- package/blocks/intent/lib/intent-utils.ts +95 -0
- package/blocks/intent/recipes/rank-intent-accounts.ts +36 -0
- package/blocks/intent/recipes/track-competitor-domain.ts +23 -0
- package/blocks/intent/recipes/who-visited-url.ts +18 -0
- package/blocks/lifecycle/README.md +79 -0
- package/blocks/lifecycle/block.json +198 -0
- package/blocks/lifecycle/components/churn-list.tsx +92 -0
- package/blocks/lifecycle/components/customer-table.tsx +119 -0
- package/blocks/lifecycle/components/lead-queue.tsx +127 -0
- package/blocks/lifecycle/components/lifecycle-board.tsx +102 -0
- package/blocks/lifecycle/components/stage-move-dialog.tsx +75 -0
- package/blocks/lifecycle/hooks/use-debounced.ts +13 -0
- package/blocks/lifecycle/hooks/use-lifecycle-stages.ts +27 -0
- package/blocks/lifecycle/lib/lifecycle.ts +99 -0
- package/blocks/lifecycle/recipes/customer-brief.ts +23 -0
- package/blocks/lifecycle/recipes/move-company-stage.ts +17 -0
- package/blocks/lifecycle/recipes/read-lifecycle-board.ts +21 -0
- package/blocks/lifecycle/recipes/unlock-stage-contacts.ts +25 -0
- package/blocks/lists/README.md +96 -0
- package/blocks/lists/block.json +166 -0
- package/blocks/lists/components/add-to-list-button.tsx +111 -0
- package/blocks/lists/components/list-picker.tsx +94 -0
- package/blocks/lists/components/list-table.tsx +160 -0
- package/blocks/lists/components/suppression-manager.tsx +153 -0
- package/blocks/lists/recipes/create-list-and-add-contacts.ts +35 -0
- package/blocks/lists/recipes/refresh-and-check-lists.ts +21 -0
- package/blocks/lists/recipes/suppress-and-reinstate.ts +30 -0
- package/blocks/mailboxes/README.md +71 -0
- package/blocks/mailboxes/block.json +187 -0
- package/blocks/mailboxes/components/connect-mailbox.tsx +284 -0
- package/blocks/mailboxes/components/domain-purchase.tsx +271 -0
- package/blocks/mailboxes/components/mailbox-list.tsx +185 -0
- package/blocks/mailboxes/components/warmup-health.tsx +232 -0
- package/blocks/mailboxes/lib/mailboxes-utils.ts +69 -0
- package/blocks/mailboxes/recipes/buy-domain-and-mailboxes.ts +64 -0
- package/blocks/mailboxes/recipes/connect-imap-mailbox.ts +38 -0
- package/blocks/mailboxes/recipes/is-it-safe-to-send.ts +17 -0
- package/blocks/mailboxes/recipes/warmup-report.ts +31 -0
- package/blocks/newsletter/README.md +122 -0
- package/blocks/newsletter/block.json +224 -0
- package/blocks/newsletter/components/issue-form.tsx +124 -0
- package/blocks/newsletter/components/issue-scheduler.tsx +152 -0
- package/blocks/newsletter/components/newsletter-editor.tsx +131 -0
- package/blocks/newsletter/components/newsletter-stats.tsx +93 -0
- package/blocks/newsletter/components/subscriber-table.tsx +173 -0
- package/blocks/newsletter/lib/newsletter-types.ts +97 -0
- package/blocks/newsletter/recipes/create-newsletter-and-draft-issue.ts +27 -0
- package/blocks/newsletter/recipes/import-audience-and-send.ts +43 -0
- package/blocks/newsletter/recipes/issue-performance-report.ts +26 -0
- package/blocks/nurture/README.md +110 -0
- package/blocks/nurture/block.json +191 -0
- package/blocks/nurture/components/nurture-builder.tsx +114 -0
- package/blocks/nurture/components/nurture-enroll.tsx +107 -0
- package/blocks/nurture/components/nurture-form.tsx +120 -0
- package/blocks/nurture/components/nurture-stats.tsx +81 -0
- package/blocks/nurture/lib/nurture-types.ts +109 -0
- package/blocks/nurture/recipes/create-a-nurture.ts +34 -0
- package/blocks/nurture/recipes/enroll-contacts-and-launch.ts +24 -0
- package/blocks/nurture/recipes/nurture-health-check.ts +28 -0
- package/blocks/objects/README.md +120 -0
- package/blocks/objects/block.json +243 -0
- package/blocks/objects/components/attribute-field-input.tsx +82 -0
- package/blocks/objects/components/attribute-form-dialog.tsx +159 -0
- package/blocks/objects/components/object-not-found.tsx +34 -0
- package/blocks/objects/components/object-record-detail.tsx +143 -0
- package/blocks/objects/components/object-schema-editor.tsx +197 -0
- package/blocks/objects/components/object-table.tsx +130 -0
- package/blocks/objects/components/record-form-dialog.tsx +112 -0
- package/blocks/objects/components/record-roles-panel.tsx +106 -0
- package/blocks/objects/hooks/use-object-schema.ts +36 -0
- package/blocks/objects/lib/object-types.ts +214 -0
- package/blocks/objects/recipes/define-object-and-add-records.ts +24 -0
- package/blocks/objects/recipes/update-audit-and-archive-records.ts +39 -0
- package/blocks/objects/recipes/upsert-records-from-your-system.ts +30 -0
- package/blocks/pages/README.md +104 -0
- package/blocks/pages/block.json +223 -0
- package/blocks/pages/components/brand-kit-editor.tsx +152 -0
- package/blocks/pages/components/content-library.tsx +163 -0
- package/blocks/pages/components/page-editor.tsx +253 -0
- package/blocks/pages/components/page-list.tsx +159 -0
- package/blocks/pages/components/seo-report.tsx +159 -0
- package/blocks/pages/lib/studio.ts +77 -0
- package/blocks/pages/recipes/audit-site-seo.ts +28 -0
- package/blocks/pages/recipes/create-and-publish-page.ts +26 -0
- package/blocks/pages/recipes/edit-page-section.ts +25 -0
- package/blocks/pages/recipes/refresh-brand-kit.ts +22 -0
- package/blocks/quotes/README.md +79 -0
- package/blocks/quotes/block.json +183 -0
- package/blocks/quotes/components/product-picker.tsx +80 -0
- package/blocks/quotes/components/quote-builder.tsx +195 -0
- package/blocks/quotes/components/quote-list.tsx +79 -0
- package/blocks/quotes/components/quote-viewer.tsx +135 -0
- package/blocks/quotes/components/signature-status.tsx +77 -0
- package/blocks/quotes/hooks/use-debounced.ts +13 -0
- package/blocks/quotes/lib/quotes.ts +142 -0
- package/blocks/quotes/recipes/create-and-send-quote.ts +44 -0
- package/blocks/quotes/recipes/revise-sent-quote.ts +18 -0
- package/blocks/quotes/recipes/track-quote-signature.ts +22 -0
- package/blocks/radar/README.md +106 -0
- package/blocks/radar/block.json +189 -0
- package/blocks/radar/components/competitor-battle-card.tsx +53 -0
- package/blocks/radar/components/competitor-tracker.tsx +158 -0
- package/blocks/radar/components/radar-overview.tsx +67 -0
- package/blocks/radar/components/radar-report.tsx +86 -0
- package/blocks/radar/hooks/use-competitors.ts +23 -0
- package/blocks/radar/lib/radar-types.ts +124 -0
- package/blocks/radar/recipes/battle-card.ts +28 -0
- package/blocks/radar/recipes/deep-dive-report.ts +32 -0
- package/blocks/radar/recipes/marketing-snapshot.ts +18 -0
- package/blocks/radar/recipes/track-and-scan.ts +39 -0
- package/blocks/search/README.md +129 -0
- package/blocks/search/block.json +185 -0
- package/blocks/search/components/filter-builder.tsx +149 -0
- package/blocks/search/components/prospect-results.tsx +175 -0
- package/blocks/search/components/save-to-list-dialog.tsx +135 -0
- package/blocks/search/components/saved-searches.tsx +95 -0
- package/blocks/search/hooks/use-filter-options.ts +41 -0
- package/blocks/search/lib/search-types.ts +149 -0
- package/blocks/search/recipes/explore-filter-values.ts +27 -0
- package/blocks/search/recipes/saved-searches.ts +34 -0
- package/blocks/search/recipes/search-and-save-to-list.ts +37 -0
- package/blocks/sequencer/README.md +87 -0
- package/blocks/sequencer/block.json +214 -0
- package/blocks/sequencer/components/enroll-dialog.tsx +93 -0
- package/blocks/sequencer/components/sequence-builder.tsx +306 -0
- package/blocks/sequencer/components/sequence-list.tsx +200 -0
- package/blocks/sequencer/components/sequence-stats.tsx +256 -0
- package/blocks/sequencer/components/step-editor.tsx +213 -0
- package/blocks/sequencer/lib/sequencer-utils.ts +168 -0
- package/blocks/sequencer/recipes/create-multichannel-sequence.ts +51 -0
- package/blocks/sequencer/recipes/launch-and-enroll.ts +44 -0
- package/blocks/sequencer/recipes/pause-when-unsafe.ts +30 -0
- package/blocks/sequencer/recipes/sequence-report.ts +22 -0
- package/blocks/social-ads/README.md +128 -0
- package/blocks/social-ads/block.json +256 -0
- package/blocks/social-ads/components/ad-campaign-list.tsx +136 -0
- package/blocks/social-ads/components/audience-sync.tsx +150 -0
- package/blocks/social-ads/components/creative-gallery.tsx +121 -0
- package/blocks/social-ads/components/post-composer.tsx +122 -0
- package/blocks/social-ads/components/social-post-list.tsx +116 -0
- package/blocks/social-ads/hooks/use-social-accounts.ts +16 -0
- package/blocks/social-ads/lib/social-ads-types.ts +167 -0
- package/blocks/social-ads/recipes/creative-to-gallery.ts +41 -0
- package/blocks/social-ads/recipes/schedule-post.ts +51 -0
- package/blocks/social-ads/recipes/sync-audience-and-launch.ts +41 -0
- package/blocks/visitors/README.md +56 -0
- package/blocks/visitors/block.json +139 -0
- package/blocks/visitors/components/snippet-install.tsx +169 -0
- package/blocks/visitors/components/traffic-chart.tsx +156 -0
- package/blocks/visitors/components/visitor-company-card.tsx +92 -0
- package/blocks/visitors/components/visitor-feed.tsx +157 -0
- package/blocks/visitors/lib/visitors-utils.ts +78 -0
- package/blocks/visitors/recipes/check-snippet-installed.ts +24 -0
- package/blocks/visitors/recipes/recent-company-visits.ts +27 -0
- package/blocks/visitors/recipes/traffic-summary.ts +24 -0
- package/blocks/webchat/README.md +87 -0
- package/blocks/webchat/block.json +134 -0
- package/blocks/webchat/components/chat-widget-config.tsx +160 -0
- package/blocks/webchat/components/conversation-list.tsx +74 -0
- package/blocks/webchat/lib/webchat-types.ts +83 -0
- package/blocks/webchat/recipes/create-bot-and-style-widget.ts +24 -0
- package/blocks/webchat/recipes/list-chat-visitors.ts +24 -0
- package/blocks/workbench/README.md +135 -0
- package/blocks/workbench/block.json +259 -0
- package/blocks/workbench/components/column-recipe-editor.tsx +183 -0
- package/blocks/workbench/components/run-progress.tsx +132 -0
- package/blocks/workbench/components/workbench-add-rows.tsx +100 -0
- package/blocks/workbench/components/workbench-grid.tsx +159 -0
- package/blocks/workbench/components/workbench-row-actions.tsx +102 -0
- package/blocks/workbench/components/workbench-table-list.tsx +105 -0
- package/blocks/workbench/hooks/use-workbench-table.ts +30 -0
- package/blocks/workbench/lib/waterfall-presets.ts +119 -0
- package/blocks/workbench/lib/workbench-types.ts +191 -0
- package/blocks/workbench/recipes/build-enriched-table.ts +65 -0
- package/blocks/workbench/recipes/import-from-search.ts +42 -0
- package/blocks/workbench/recipes/scrape-into-table.ts +36 -0
- package/blocks/workbench/recipes/waterfall-column.ts +55 -0
- package/blocks/workflows/README.md +100 -0
- package/blocks/workflows/block.json +188 -0
- package/blocks/workflows/components/run-history.tsx +136 -0
- package/blocks/workflows/components/trigger-config.tsx +134 -0
- package/blocks/workflows/components/workflow-editor.tsx +142 -0
- package/blocks/workflows/components/workflow-list.tsx +153 -0
- package/blocks/workflows/hooks/use-workflow.ts +51 -0
- package/blocks/workflows/lib/graph.ts +187 -0
- package/blocks/workflows/recipes/create-workflow.ts +35 -0
- package/blocks/workflows/recipes/find-failed-runs.ts +15 -0
- package/blocks/workflows/recipes/run-workflow-and-wait.ts +32 -0
- package/blocks/workflows/recipes/set-trigger.ts +22 -0
- package/dist/cli.js +5330 -29
- package/dist/index.browser.js +7740 -0
- package/dist/index.browser.js.map +1 -0
- package/dist/index.browser.mjs +7717 -0
- package/dist/index.browser.mjs.map +1 -0
- package/dist/index.d.mts +1440 -21
- package/dist/index.d.ts +1440 -21
- package/dist/index.js +42 -3
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +42 -3
- package/dist/index.mjs.map +1 -1
- package/dist/react.browser.js +7598 -0
- package/dist/react.browser.js.map +1 -0
- package/dist/react.browser.mjs +7568 -0
- package/dist/react.browser.mjs.map +1 -0
- package/dist/react.d.mts +1340 -18
- package/dist/react.d.ts +1340 -18
- package/dist/react.js +41 -2
- package/dist/react.js.map +1 -1
- package/dist/react.mjs +41 -2
- package/dist/react.mjs.map +1 -1
- package/dist/types/agentSetup.d.ts +6 -6
- package/dist/types/blocks/add.d.ts +53 -0
- package/dist/types/blocks/create.d.ts +46 -0
- package/dist/types/blocks/doctor.d.ts +26 -0
- package/dist/types/blocks/registry.d.ts +134 -0
- package/dist/types/cli.d.ts +16 -0
- package/dist/types/core.d.ts +1 -1
- package/dist/types/crypto-browser.d.ts +2 -0
- package/dist/types/crypto-node.d.ts +6 -0
- package/dist/types/react.d.ts +1 -1
- package/dist/types/version.d.ts +2 -2
- package/dist/types/webhooks.d.ts +2 -2
- package/dist/ui/index.d.mts +54 -0
- package/dist/ui/index.d.ts +54 -0
- package/dist/ui/index.js +105 -0
- package/dist/ui/index.js.map +1 -0
- package/dist/ui/index.mjs +44 -0
- package/dist/ui/index.mjs.map +1 -0
- package/dist/ui/lib/compose-refs.d.mts +19 -0
- package/dist/ui/lib/compose-refs.d.ts +19 -0
- package/dist/ui/lib/compose-refs.js +76 -0
- package/dist/ui/lib/compose-refs.js.map +1 -0
- package/dist/ui/lib/compose-refs.mjs +41 -0
- package/dist/ui/lib/compose-refs.mjs.map +1 -0
- package/dist/ui/lib/use-mobile.d.mts +4 -0
- package/dist/ui/lib/use-mobile.d.ts +4 -0
- package/dist/ui/lib/use-mobile.js +57 -0
- package/dist/ui/lib/use-mobile.js.map +1 -0
- package/dist/ui/lib/use-mobile.mjs +22 -0
- package/dist/ui/lib/use-mobile.mjs.map +1 -0
- package/dist/ui/lib/utils.d.mts +10 -0
- package/dist/ui/lib/utils.d.ts +10 -0
- package/dist/ui/lib/utils.js +33 -0
- package/dist/ui/lib/utils.js.map +1 -0
- package/dist/ui/lib/utils.mjs +9 -0
- package/dist/ui/lib/utils.mjs.map +1 -0
- package/dist/ui/primitives/alert-dialog.d.mts +22 -0
- package/dist/ui/primitives/alert-dialog.d.ts +22 -0
- package/dist/ui/primitives/alert-dialog.js +163 -0
- package/dist/ui/primitives/alert-dialog.js.map +1 -0
- package/dist/ui/primitives/alert-dialog.mjs +119 -0
- package/dist/ui/primitives/alert-dialog.mjs.map +1 -0
- package/dist/ui/primitives/avatar.d.mts +8 -0
- package/dist/ui/primitives/avatar.d.ts +8 -0
- package/dist/ui/primitives/avatar.js +91 -0
- package/dist/ui/primitives/avatar.js.map +1 -0
- package/dist/ui/primitives/avatar.mjs +55 -0
- package/dist/ui/primitives/avatar.mjs.map +1 -0
- package/dist/ui/primitives/breadcrumb.d.mts +13 -0
- package/dist/ui/primitives/breadcrumb.d.ts +13 -0
- package/dist/ui/primitives/breadcrumb.js +127 -0
- package/dist/ui/primitives/breadcrumb.js.map +1 -0
- package/dist/ui/primitives/breadcrumb.mjs +97 -0
- package/dist/ui/primitives/breadcrumb.mjs.map +1 -0
- package/dist/ui/primitives/button.d.mts +15 -0
- package/dist/ui/primitives/button.d.ts +15 -0
- package/dist/ui/primitives/button.js +91 -0
- package/dist/ui/primitives/button.js.map +1 -0
- package/dist/ui/primitives/button.mjs +56 -0
- package/dist/ui/primitives/button.mjs.map +1 -0
- package/dist/ui/primitives/card.d.mts +10 -0
- package/dist/ui/primitives/card.d.ts +10 -0
- package/dist/ui/primitives/card.js +101 -0
- package/dist/ui/primitives/card.js.map +1 -0
- package/dist/ui/primitives/card.mjs +62 -0
- package/dist/ui/primitives/card.mjs.map +1 -0
- package/dist/ui/primitives/carousel.d.mts +45 -0
- package/dist/ui/primitives/carousel.d.ts +45 -0
- package/dist/ui/primitives/carousel.js +300 -0
- package/dist/ui/primitives/carousel.js.map +1 -0
- package/dist/ui/primitives/carousel.mjs +261 -0
- package/dist/ui/primitives/carousel.mjs.map +1 -0
- package/dist/ui/primitives/chart.d.mts +68 -0
- package/dist/ui/primitives/chart.d.ts +68 -0
- package/dist/ui/primitives/chart.js +280 -0
- package/dist/ui/primitives/chart.js.map +1 -0
- package/dist/ui/primitives/chart.mjs +241 -0
- package/dist/ui/primitives/chart.mjs.map +1 -0
- package/dist/ui/primitives/collapsible.d.mts +8 -0
- package/dist/ui/primitives/collapsible.d.ts +8 -0
- package/dist/ui/primitives/collapsible.js +72 -0
- package/dist/ui/primitives/collapsible.js.map +1 -0
- package/dist/ui/primitives/collapsible.mjs +36 -0
- package/dist/ui/primitives/collapsible.mjs.map +1 -0
- package/dist/ui/primitives/context-menu.d.mts +29 -0
- package/dist/ui/primitives/context-menu.d.ts +29 -0
- package/dist/ui/primitives/context-menu.js +203 -0
- package/dist/ui/primitives/context-menu.js.map +1 -0
- package/dist/ui/primitives/context-menu.mjs +155 -0
- package/dist/ui/primitives/context-menu.mjs.map +1 -0
- package/dist/ui/primitives/dialog.d.mts +26 -0
- package/dist/ui/primitives/dialog.d.ts +26 -0
- package/dist/ui/primitives/dialog.js +192 -0
- package/dist/ui/primitives/dialog.js.map +1 -0
- package/dist/ui/primitives/dialog.mjs +148 -0
- package/dist/ui/primitives/dialog.mjs.map +1 -0
- package/dist/ui/primitives/drawer.d.mts +26 -0
- package/dist/ui/primitives/drawer.d.ts +26 -0
- package/dist/ui/primitives/drawer.js +146 -0
- package/dist/ui/primitives/drawer.js.map +1 -0
- package/dist/ui/primitives/drawer.mjs +103 -0
- package/dist/ui/primitives/drawer.mjs.map +1 -0
- package/dist/ui/primitives/input.d.mts +5 -0
- package/dist/ui/primitives/input.d.ts +5 -0
- package/dist/ui/primitives/input.js +46 -0
- package/dist/ui/primitives/input.js.map +1 -0
- package/dist/ui/primitives/input.mjs +22 -0
- package/dist/ui/primitives/input.mjs.map +1 -0
- package/dist/ui/primitives/item.d.mts +27 -0
- package/dist/ui/primitives/item.d.ts +27 -0
- package/dist/ui/primitives/item.js +221 -0
- package/dist/ui/primitives/item.js.map +1 -0
- package/dist/ui/primitives/item.mjs +188 -0
- package/dist/ui/primitives/item.mjs.map +1 -0
- package/dist/ui/primitives/kbd.d.mts +15 -0
- package/dist/ui/primitives/kbd.d.ts +15 -0
- package/dist/ui/primitives/kbd.js +45 -0
- package/dist/ui/primitives/kbd.js.map +1 -0
- package/dist/ui/primitives/kbd.mjs +21 -0
- package/dist/ui/primitives/kbd.mjs.map +1 -0
- package/dist/ui/primitives/label.d.mts +6 -0
- package/dist/ui/primitives/label.d.ts +6 -0
- package/dist/ui/primitives/label.js +55 -0
- package/dist/ui/primitives/label.js.map +1 -0
- package/dist/ui/primitives/label.mjs +21 -0
- package/dist/ui/primitives/label.mjs.map +1 -0
- package/dist/ui/primitives/resizable.d.mts +10 -0
- package/dist/ui/primitives/resizable.d.ts +10 -0
- package/dist/ui/primitives/resizable.js +68 -0
- package/dist/ui/primitives/resizable.js.map +1 -0
- package/dist/ui/primitives/resizable.mjs +42 -0
- package/dist/ui/primitives/resizable.mjs.map +1 -0
- package/dist/ui/primitives/save-status.d.mts +31 -0
- package/dist/ui/primitives/save-status.d.ts +31 -0
- package/dist/ui/primitives/save-status.js +150 -0
- package/dist/ui/primitives/save-status.js.map +1 -0
- package/dist/ui/primitives/save-status.mjs +126 -0
- package/dist/ui/primitives/save-status.mjs.map +1 -0
- package/dist/ui/primitives/separator.d.mts +6 -0
- package/dist/ui/primitives/separator.d.ts +6 -0
- package/dist/ui/primitives/separator.js +62 -0
- package/dist/ui/primitives/separator.js.map +1 -0
- package/dist/ui/primitives/separator.mjs +28 -0
- package/dist/ui/primitives/separator.mjs.map +1 -0
- package/dist/ui/primitives/sheet.d.mts +16 -0
- package/dist/ui/primitives/sheet.d.ts +16 -0
- package/dist/ui/primitives/sheet.js +170 -0
- package/dist/ui/primitives/sheet.js.map +1 -0
- package/dist/ui/primitives/sheet.mjs +129 -0
- package/dist/ui/primitives/sheet.mjs.map +1 -0
- package/dist/ui/primitives/sidebar.d.mts +74 -0
- package/dist/ui/primitives/sidebar.d.ts +74 -0
- package/dist/ui/primitives/sidebar.js +697 -0
- package/dist/ui/primitives/sidebar.js.map +1 -0
- package/dist/ui/primitives/sidebar.mjs +651 -0
- package/dist/ui/primitives/sidebar.mjs.map +1 -0
- package/dist/ui/primitives/skeleton.d.mts +5 -0
- package/dist/ui/primitives/skeleton.d.ts +5 -0
- package/dist/ui/primitives/skeleton.js +40 -0
- package/dist/ui/primitives/skeleton.js.map +1 -0
- package/dist/ui/primitives/skeleton.mjs +16 -0
- package/dist/ui/primitives/skeleton.mjs.map +1 -0
- package/dist/ui/primitives/slider.d.mts +20 -0
- package/dist/ui/primitives/slider.d.ts +20 -0
- package/dist/ui/primitives/slider.js +112 -0
- package/dist/ui/primitives/slider.js.map +1 -0
- package/dist/ui/primitives/slider.mjs +78 -0
- package/dist/ui/primitives/slider.mjs.map +1 -0
- package/dist/ui/primitives/sortable.d.mts +50 -0
- package/dist/ui/primitives/sortable.d.ts +50 -0
- package/dist/ui/primitives/sortable.js +456 -0
- package/dist/ui/primitives/sortable.js.map +1 -0
- package/dist/ui/primitives/sortable.mjs +435 -0
- package/dist/ui/primitives/sortable.mjs.map +1 -0
- package/dist/ui/primitives/switch.d.mts +6 -0
- package/dist/ui/primitives/switch.d.ts +6 -0
- package/dist/ui/primitives/switch.js +67 -0
- package/dist/ui/primitives/switch.js.map +1 -0
- package/dist/ui/primitives/switch.mjs +33 -0
- package/dist/ui/primitives/switch.mjs.map +1 -0
- package/dist/ui/primitives/table.d.mts +12 -0
- package/dist/ui/primitives/table.d.ts +12 -0
- package/dist/ui/primitives/table.js +130 -0
- package/dist/ui/primitives/table.js.map +1 -0
- package/dist/ui/primitives/table.mjs +89 -0
- package/dist/ui/primitives/table.mjs.map +1 -0
- package/dist/ui/primitives/tabs.d.mts +9 -0
- package/dist/ui/primitives/tabs.d.ts +9 -0
- package/dist/ui/primitives/tabs.js +86 -0
- package/dist/ui/primitives/tabs.js.map +1 -0
- package/dist/ui/primitives/tabs.mjs +49 -0
- package/dist/ui/primitives/tabs.mjs.map +1 -0
- package/dist/ui/primitives/textarea.d.mts +5 -0
- package/dist/ui/primitives/textarea.d.ts +5 -0
- package/dist/ui/primitives/textarea.js +43 -0
- package/dist/ui/primitives/textarea.js.map +1 -0
- package/dist/ui/primitives/textarea.mjs +19 -0
- package/dist/ui/primitives/textarea.mjs.map +1 -0
- package/dist/ui/primitives/tooltip.d.mts +9 -0
- package/dist/ui/primitives/tooltip.d.ts +9 -0
- package/dist/ui/primitives/tooltip.js +94 -0
- package/dist/ui/primitives/tooltip.js.map +1 -0
- package/dist/ui/primitives/tooltip.mjs +57 -0
- package/dist/ui/primitives/tooltip.mjs.map +1 -0
- package/docs/agents.md +72 -29
- package/docs/api/README.md +6 -3
- package/docs/api/appObjects.d.ts +1 -1
- package/docs/api/appObjects.md +1 -1
- package/docs/api/by-path.md +29 -0
- package/docs/api/objects.d.ts +72 -5
- package/docs/api/objects.md +84 -7
- package/docs/api/processLists.d.ts +247 -0
- package/docs/api/processLists.md +409 -0
- package/docs/api/reports.d.ts +476 -0
- package/docs/api/reports.md +539 -0
- package/docs/api/scheduleBuy.d.ts +12 -8
- package/docs/api/scheduleBuy.md +13 -10
- package/docs/api/seo.d.ts +406 -3
- package/docs/api/seo.md +505 -1
- package/docs/api/workspaceBackup.d.ts +324 -0
- package/docs/api/workspaceBackup.md +427 -0
- package/package.json +174 -5
- package/stacks/6sense.json +46 -0
- package/stacks/README.md +20 -0
- package/stacks/apollo.json +128 -0
- package/stacks/clay.json +59 -0
- package/stacks/gong.json +72 -0
- package/stacks/hubspot.json +162 -0
- package/stacks/intercom.json +53 -0
- package/stacks/mailchimp.json +71 -0
- package/stacks/outreach.json +86 -0
- package/stacks/salesforce.json +94 -0
- package/stacks/stack.schema.json +32 -0
- package/stacks/starter.json +22 -0
- package/styles/styles.css +2 -0
- package/styles/theme.css +181 -0
- package/styles/tokens.css +284 -0
- package/templates/next-app/README.md.tmpl +35 -0
- package/templates/next-app/_pages/redirect-page.tsx.tmpl +5 -0
- package/templates/next-app/_pages/route-page.tsx.tmpl +9 -0
- package/templates/next-app/app/globals.css.tmpl +8 -0
- package/templates/next-app/app/layout.tsx.tmpl +19 -0
- package/templates/next-app/components/app-shell.tsx.tmpl +34 -0
- package/templates/next-app/dot-env.local.example.tmpl +6 -0
- package/templates/next-app/dot-gitignore.tmpl +7 -0
- package/templates/next-app/lib/nav.ts.tmpl +2 -0
- package/templates/next-app/next.config.mjs.tmpl +4 -0
- package/templates/next-app/package.json.tmpl +25 -0
- package/templates/next-app/postcss.config.mjs.tmpl +4 -0
- package/templates/next-app/tsconfig.json.tmpl +21 -0
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* WebhookManager: subscribe your server to graph8 events, see delivery attempts, rotate the signing secret.
|
|
5
|
+
*
|
|
6
|
+
* SDK operations, in order:
|
|
7
|
+
* 1. listWebhooks GET /webhooks (read)
|
|
8
|
+
* 2. listWebhookEvents GET /webhooks/events (read; the event names, grouped by category)
|
|
9
|
+
* 3. createWebhook POST /webhooks (external: confirmed; the secret is shown ONCE)
|
|
10
|
+
* 4. updateWebhook PATCH /webhooks/{webhook_id} (external: confirmed; pause or resume)
|
|
11
|
+
* 5. rotateWebhookSecret POST /webhooks/{webhook_id}/rotate-secret (external: confirmed; new secret shown once)
|
|
12
|
+
* 6. listWebhookDeliveries GET /webhooks/{webhook_id}/deliveries (read; attempts, status, response code)
|
|
13
|
+
* 7. deleteWebhook DELETE /webhooks/{webhook_id} (destructive: confirmed)
|
|
14
|
+
*
|
|
15
|
+
* Tiers: 3-5 change where graph8 sends your data, 7 removes the subscription: each opens ConfirmSpendDialog.
|
|
16
|
+
* VERIFY on the server, never in the browser: deliveries are signed with the secret. See the "Verify
|
|
17
|
+
* deliveries" note in the UI and recipes/verify-webhook-route.ts (`g8.webhooks.constructEvent`).
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import { useState } from "react";
|
|
21
|
+
import { Button, Input, Skeleton } from "@graph8/sdk/ui";
|
|
22
|
+
import { g8Delete, g8Get, g8Patch, g8Post } from "@/g8/lib/g8-client";
|
|
23
|
+
import { useG8Query } from "@/g8/hooks/use-g8-query";
|
|
24
|
+
import { useG8Mutation } from "@/g8/hooks/use-g8-mutation";
|
|
25
|
+
import { ApiError } from "@/g8/components/api-error";
|
|
26
|
+
import { ConfirmSpendDialog } from "@/g8/components/confirm-spend-dialog";
|
|
27
|
+
import type { ConfirmTier } from "@/g8/components/confirm-spend-dialog";
|
|
28
|
+
import { EmptyState } from "@/g8/components/empty-state";
|
|
29
|
+
import { arrayOf, formatWhen } from "../lib/integrations-utils";
|
|
30
|
+
|
|
31
|
+
export interface WebhookManagerProps {
|
|
32
|
+
/** Pre-fill the endpoint URL of the create form (your /api/webhooks/graph8 route). */
|
|
33
|
+
defaultUrl?: string;
|
|
34
|
+
className?: string;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
interface Webhook {
|
|
38
|
+
id: string;
|
|
39
|
+
url: string;
|
|
40
|
+
name?: string | null;
|
|
41
|
+
events?: string[];
|
|
42
|
+
is_active?: boolean;
|
|
43
|
+
secret?: string | null;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
interface EventType {
|
|
47
|
+
event: string;
|
|
48
|
+
category?: string | null;
|
|
49
|
+
description?: string | null;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
interface Delivery {
|
|
53
|
+
id: string;
|
|
54
|
+
event: string;
|
|
55
|
+
status: string;
|
|
56
|
+
attempts?: number;
|
|
57
|
+
response_code?: number | null;
|
|
58
|
+
error_message?: string | null;
|
|
59
|
+
created_at?: string | null;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
type Pending =
|
|
63
|
+
| { kind: "create"; url: string; name: string; events: string[] }
|
|
64
|
+
| { kind: "toggle"; hook: Webhook }
|
|
65
|
+
| { kind: "rotate"; hook: Webhook }
|
|
66
|
+
| { kind: "delete"; hook: Webhook };
|
|
67
|
+
|
|
68
|
+
function copyFor(p: Pending): { tier: ConfirmTier; title: string; description: string; label: string } {
|
|
69
|
+
switch (p.kind) {
|
|
70
|
+
case "create":
|
|
71
|
+
return {
|
|
72
|
+
tier: "external",
|
|
73
|
+
title: `Send ${p.events.length} event type${p.events.length === 1 ? "" : "s"} to ${p.url}`,
|
|
74
|
+
description: "graph8 will POST signed events to this URL as they happen.",
|
|
75
|
+
label: "Create webhook",
|
|
76
|
+
};
|
|
77
|
+
case "toggle":
|
|
78
|
+
return {
|
|
79
|
+
tier: "external",
|
|
80
|
+
title: `${p.hook.is_active === false ? "Resume" : "Pause"} deliveries to ${p.hook.url}`,
|
|
81
|
+
description:
|
|
82
|
+
p.hook.is_active === false
|
|
83
|
+
? "graph8 starts sending events to this URL again."
|
|
84
|
+
: "Events stop being sent to this URL until you resume.",
|
|
85
|
+
label: p.hook.is_active === false ? "Resume" : "Pause",
|
|
86
|
+
};
|
|
87
|
+
case "rotate":
|
|
88
|
+
return {
|
|
89
|
+
tier: "external",
|
|
90
|
+
title: `Rotate the signing secret for ${p.hook.url}`,
|
|
91
|
+
description: "The old secret stops working immediately. Update your server before the next delivery.",
|
|
92
|
+
label: "Rotate secret",
|
|
93
|
+
};
|
|
94
|
+
case "delete":
|
|
95
|
+
return {
|
|
96
|
+
tier: "destructive",
|
|
97
|
+
title: `Delete the webhook for ${p.hook.url}`,
|
|
98
|
+
description: "Deliveries stop and the subscription cannot be restored.",
|
|
99
|
+
label: "Delete webhook",
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function Deliveries({ hook }: { hook: Webhook }) {
|
|
105
|
+
const list = useG8Query((signal) => g8Get(`/webhooks/${hook.id}/deliveries`, { page: 1, limit: 20 }, { signal }), [hook.id]);
|
|
106
|
+
if (list.loading) return <Skeleton className="h-16 w-full" />;
|
|
107
|
+
if (list.error) return <ApiError error={list.error} onRetry={() => void list.refetch()} />;
|
|
108
|
+
const rows = arrayOf<Delivery>(list.data);
|
|
109
|
+
if (rows.length === 0) return <p className="text-sm text-gray-500">No deliveries yet.</p>;
|
|
110
|
+
return (
|
|
111
|
+
<table className="w-full text-sm" aria-label="Deliveries">
|
|
112
|
+
<thead>
|
|
113
|
+
<tr className="text-left text-gray-500">
|
|
114
|
+
<th>Event</th>
|
|
115
|
+
<th>Status</th>
|
|
116
|
+
<th>Code</th>
|
|
117
|
+
<th>Attempts</th>
|
|
118
|
+
<th>When</th>
|
|
119
|
+
</tr>
|
|
120
|
+
</thead>
|
|
121
|
+
<tbody>
|
|
122
|
+
{rows.map((d) => (
|
|
123
|
+
<tr key={d.id} className="border-t">
|
|
124
|
+
<td>{d.event}</td>
|
|
125
|
+
<td>{d.status}</td>
|
|
126
|
+
<td>{d.response_code ?? "-"}</td>
|
|
127
|
+
<td>{d.attempts ?? 1}</td>
|
|
128
|
+
<td title={d.error_message ?? undefined}>{formatWhen(d.created_at)}</td>
|
|
129
|
+
</tr>
|
|
130
|
+
))}
|
|
131
|
+
</tbody>
|
|
132
|
+
</table>
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export function WebhookManager({ defaultUrl = "", className }: WebhookManagerProps) {
|
|
137
|
+
const hooks = useG8Query((signal) => g8Get("/webhooks", undefined, { signal }), []);
|
|
138
|
+
const events = useG8Query((signal) => g8Get("/webhooks/events", undefined, { signal }), []);
|
|
139
|
+
const [url, setUrl] = useState(defaultUrl);
|
|
140
|
+
const [name, setName] = useState("");
|
|
141
|
+
const [chosen, setChosen] = useState<string[]>([]);
|
|
142
|
+
const [pending, setPending] = useState<Pending | null>(null);
|
|
143
|
+
const [secret, setSecret] = useState<{ url: string; value: string } | null>(null);
|
|
144
|
+
const [openDeliveries, setOpenDeliveries] = useState<string | null>(null);
|
|
145
|
+
|
|
146
|
+
const run = useG8Mutation(async (p: Pending) => {
|
|
147
|
+
if (p.kind === "create") {
|
|
148
|
+
const res = await g8Post<Webhook>("/webhooks", { url: p.url, name: p.name || undefined, events: p.events });
|
|
149
|
+
if (res.data?.secret) setSecret({ url: p.url, value: res.data.secret });
|
|
150
|
+
setUrl("");
|
|
151
|
+
setName("");
|
|
152
|
+
setChosen([]);
|
|
153
|
+
} else if (p.kind === "toggle") {
|
|
154
|
+
await g8Patch(`/webhooks/${p.hook.id}`, { is_active: p.hook.is_active === false });
|
|
155
|
+
} else if (p.kind === "rotate") {
|
|
156
|
+
const res = await g8Post<Webhook>(`/webhooks/${p.hook.id}/rotate-secret`);
|
|
157
|
+
if (res.data?.secret) setSecret({ url: p.hook.url, value: res.data.secret });
|
|
158
|
+
} else {
|
|
159
|
+
await g8Delete(`/webhooks/${p.hook.id}`);
|
|
160
|
+
setOpenDeliveries((open) => (open === p.hook.id ? null : open));
|
|
161
|
+
}
|
|
162
|
+
await hooks.refetch();
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
const list = arrayOf<Webhook>(hooks.data);
|
|
166
|
+
const byCategory = arrayOf<EventType>(events.data).reduce<Record<string, EventType[]>>(
|
|
167
|
+
(acc, e) => ({ ...acc, [e.category ?? "other"]: [...(acc[e.category ?? "other"] ?? []), e] }),
|
|
168
|
+
{},
|
|
169
|
+
);
|
|
170
|
+
const urlOk = /^https:\/\/\S+$/.test(url.trim());
|
|
171
|
+
const toggle = (event: string) => setChosen((c) => (c.includes(event) ? c.filter((x) => x !== event) : [...c, event]));
|
|
172
|
+
const confirm = pending ? copyFor(pending) : null;
|
|
173
|
+
|
|
174
|
+
return (
|
|
175
|
+
<div className={`space-y-4 ${className ?? ""}`}>
|
|
176
|
+
<section className="space-y-3 rounded border border-gray-200 p-3" aria-label="New webhook">
|
|
177
|
+
<h3 className="text-sm font-semibold">New webhook</h3>
|
|
178
|
+
<div className="grid gap-2 md:grid-cols-2">
|
|
179
|
+
<Input
|
|
180
|
+
aria-label="Endpoint URL"
|
|
181
|
+
placeholder="https://yourapp.com/api/webhooks/graph8"
|
|
182
|
+
value={url}
|
|
183
|
+
onChange={(e) => setUrl(e.target.value)}
|
|
184
|
+
/>
|
|
185
|
+
<Input aria-label="Name" placeholder="Name (optional)" value={name} onChange={(e) => setName(e.target.value)} />
|
|
186
|
+
</div>
|
|
187
|
+
{url && !urlOk ? <p className="text-xs text-red-700">The endpoint must be an https:// URL.</p> : null}
|
|
188
|
+
{events.loading ? (
|
|
189
|
+
<Skeleton className="h-12 w-full" />
|
|
190
|
+
) : (
|
|
191
|
+
<div className="space-y-2">
|
|
192
|
+
{Object.entries(byCategory).map(([category, items]) => (
|
|
193
|
+
<fieldset key={category}>
|
|
194
|
+
<legend className="text-xs font-medium uppercase text-gray-500">{category}</legend>
|
|
195
|
+
<div className="flex flex-wrap gap-x-4 gap-y-1">
|
|
196
|
+
{items.map((e) => (
|
|
197
|
+
<label key={e.event} className="flex items-center gap-1 text-sm" title={e.description ?? undefined}>
|
|
198
|
+
<input type="checkbox" checked={chosen.includes(e.event)} onChange={() => toggle(e.event)} />
|
|
199
|
+
{e.event}
|
|
200
|
+
</label>
|
|
201
|
+
))}
|
|
202
|
+
</div>
|
|
203
|
+
</fieldset>
|
|
204
|
+
))}
|
|
205
|
+
</div>
|
|
206
|
+
)}
|
|
207
|
+
<Button
|
|
208
|
+
disabled={!urlOk || chosen.length === 0}
|
|
209
|
+
onClick={() => setPending({ kind: "create", url: url.trim(), name: name.trim(), events: chosen })}
|
|
210
|
+
>
|
|
211
|
+
Create webhook
|
|
212
|
+
</Button>
|
|
213
|
+
</section>
|
|
214
|
+
|
|
215
|
+
{secret ? (
|
|
216
|
+
<div role="alert" className="rounded border border-amber-300 bg-amber-50 p-3 text-sm">
|
|
217
|
+
<p className="font-medium">Signing secret for {secret.url}. It is shown once.</p>
|
|
218
|
+
<code className="mt-1 block break-all rounded bg-white p-2">{secret.value}</code>
|
|
219
|
+
<p className="mt-1 text-xs text-gray-600">
|
|
220
|
+
Store it in your server environment as G8_WEBHOOK_SECRET. Never put it in browser code.
|
|
221
|
+
</p>
|
|
222
|
+
<Button size="sm" variant="outline" className="mt-2" onClick={() => setSecret(null)}>
|
|
223
|
+
I saved it
|
|
224
|
+
</Button>
|
|
225
|
+
</div>
|
|
226
|
+
) : null}
|
|
227
|
+
{run.error ? <ApiError error={run.error} onRetry={run.reset} /> : null}
|
|
228
|
+
|
|
229
|
+
{hooks.error ? (
|
|
230
|
+
<ApiError error={hooks.error} onRetry={() => void hooks.refetch()} />
|
|
231
|
+
) : hooks.loading && !hooks.data ? (
|
|
232
|
+
<Skeleton className="h-24 w-full" />
|
|
233
|
+
) : list.length === 0 ? (
|
|
234
|
+
<EmptyState title="No webhooks yet" description="Create one above to receive events like replies and meetings on your server." />
|
|
235
|
+
) : (
|
|
236
|
+
<ul className="divide-y rounded border border-gray-200">
|
|
237
|
+
{list.map((h) => (
|
|
238
|
+
<li key={h.id} className="space-y-2 px-3 py-3">
|
|
239
|
+
<div className="flex flex-wrap items-center justify-between gap-2">
|
|
240
|
+
<div>
|
|
241
|
+
<div className="font-medium">{h.name || h.url}</div>
|
|
242
|
+
<div className="text-xs text-gray-500">
|
|
243
|
+
{h.name ? `${h.url} - ` : ""}
|
|
244
|
+
{(h.events ?? []).length} events - {h.is_active === false ? "paused" : "active"}
|
|
245
|
+
</div>
|
|
246
|
+
</div>
|
|
247
|
+
<div className="flex gap-1">
|
|
248
|
+
<Button size="sm" variant="outline" onClick={() => setOpenDeliveries(openDeliveries === h.id ? null : h.id)}>
|
|
249
|
+
Deliveries
|
|
250
|
+
</Button>
|
|
251
|
+
<Button size="sm" variant="outline" onClick={() => setPending({ kind: "toggle", hook: h })}>
|
|
252
|
+
{h.is_active === false ? "Resume" : "Pause"}
|
|
253
|
+
</Button>
|
|
254
|
+
<Button size="sm" variant="outline" onClick={() => setPending({ kind: "rotate", hook: h })}>
|
|
255
|
+
Rotate secret
|
|
256
|
+
</Button>
|
|
257
|
+
<Button
|
|
258
|
+
size="sm"
|
|
259
|
+
variant="ghost"
|
|
260
|
+
aria-label={`Delete webhook ${h.url}`}
|
|
261
|
+
onClick={() => setPending({ kind: "delete", hook: h })}
|
|
262
|
+
>
|
|
263
|
+
Delete
|
|
264
|
+
</Button>
|
|
265
|
+
</div>
|
|
266
|
+
</div>
|
|
267
|
+
{openDeliveries === h.id ? <Deliveries hook={h} /> : null}
|
|
268
|
+
</li>
|
|
269
|
+
))}
|
|
270
|
+
</ul>
|
|
271
|
+
)}
|
|
272
|
+
|
|
273
|
+
<details className="rounded border border-gray-200 p-3 text-sm">
|
|
274
|
+
<summary className="cursor-pointer font-medium">Verify deliveries (server side)</summary>
|
|
275
|
+
<p className="mt-2 text-gray-600">
|
|
276
|
+
Check the signature on the raw body before trusting an event. Deliveries repeat, so deduplicate on the event id.
|
|
277
|
+
</p>
|
|
278
|
+
<pre className="mt-2 overflow-x-auto rounded bg-gray-950 p-3 text-xs text-gray-100">
|
|
279
|
+
<code>{`const event = g8.webhooks.constructEvent(rawBody,
|
|
280
|
+
req.headers.get("x-studio-signature") ?? "",
|
|
281
|
+
req.headers.get("x-studio-timestamp") ?? "",
|
|
282
|
+
process.env.G8_WEBHOOK_SECRET!, { toleranceSeconds: 300 });`}</code>
|
|
283
|
+
</pre>
|
|
284
|
+
</details>
|
|
285
|
+
|
|
286
|
+
{pending && confirm ? (
|
|
287
|
+
<ConfirmSpendDialog
|
|
288
|
+
open
|
|
289
|
+
tier={confirm.tier}
|
|
290
|
+
title={confirm.title}
|
|
291
|
+
description={confirm.description}
|
|
292
|
+
confirmLabel={confirm.label}
|
|
293
|
+
confirming={run.loading}
|
|
294
|
+
onCancel={() => setPending(null)}
|
|
295
|
+
onConfirm={() => void run.mutate(pending).then(() => setPending(null))}
|
|
296
|
+
/>
|
|
297
|
+
) : null}
|
|
298
|
+
</div>
|
|
299
|
+
);
|
|
300
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
/** Types and helpers shared by the integrations components. */
|
|
4
|
+
|
|
5
|
+
export const CRM_PROVIDERS = ["hubspot", "salesforce", "pipedrive", "zoho", "sugarcrm"] as const;
|
|
6
|
+
export type CrmProvider = (typeof CRM_PROVIDERS)[number];
|
|
7
|
+
|
|
8
|
+
export const PROVIDER_LABELS: Record<CrmProvider, string> = {
|
|
9
|
+
hubspot: "HubSpot",
|
|
10
|
+
salesforce: "Salesforce",
|
|
11
|
+
pipedrive: "Pipedrive",
|
|
12
|
+
zoho: "Zoho CRM",
|
|
13
|
+
sugarcrm: "SugarCRM",
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
/** Where the person completes an OAuth connection. The API has no way to run the consent screen for you. */
|
|
17
|
+
export const GRAPH8_APP_INTEGRATIONS_URL = "https://app.graph8.com/studio/settings?tab=integrations";
|
|
18
|
+
|
|
19
|
+
export interface CrmSyncRow {
|
|
20
|
+
provider: string;
|
|
21
|
+
status: string;
|
|
22
|
+
connection_id?: string | null;
|
|
23
|
+
connected_at?: string | null;
|
|
24
|
+
last_sync_at?: string | null;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface CrmStatus {
|
|
28
|
+
connected: boolean;
|
|
29
|
+
connection_id?: string | null;
|
|
30
|
+
crm_type?: string | null;
|
|
31
|
+
last_sync_at?: string | null;
|
|
32
|
+
message?: string | null;
|
|
33
|
+
provider: string;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface ConnectionRow {
|
|
37
|
+
connection_id?: string;
|
|
38
|
+
provider?: string;
|
|
39
|
+
provider_config_key?: string;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function arrayOf<T>(envelope: unknown): T[] {
|
|
43
|
+
const data = (envelope as { data?: unknown } | null)?.data;
|
|
44
|
+
if (Array.isArray(data)) return data as T[];
|
|
45
|
+
const items = (data as { items?: unknown } | null)?.items;
|
|
46
|
+
return Array.isArray(items) ? (items as T[]) : [];
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function formatWhen(value: string | null | undefined): string {
|
|
50
|
+
if (!value) return "never";
|
|
51
|
+
const d = new Date(value);
|
|
52
|
+
return Number.isNaN(d.getTime()) ? value : d.toLocaleString();
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** Open-shaped status objects: true when any of the usual "connected" flags is set. */
|
|
56
|
+
export function looksConnected(status: unknown): boolean {
|
|
57
|
+
const data = (status as { data?: Record<string, unknown> } | null)?.data;
|
|
58
|
+
if (!data) return false;
|
|
59
|
+
return Boolean(data.connected ?? data.is_connected ?? data.installed ?? data.workspace ?? data.team ?? false);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export const TERMINAL_JOB_STATES = ["completed", "complete", "success", "succeeded", "failed", "error", "cancelled", "canceled"];
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @title Check every CRM connection is healthy
|
|
3
|
+
* @when A scheduled health check or a status page. Reads only (no CRM writes). `connected: false` means the
|
|
4
|
+
* OAuth grant is gone: the human must reconnect it in the graph8 app, the API cannot do that.
|
|
5
|
+
*/
|
|
6
|
+
import { g8 } from "@graph8/sdk";
|
|
7
|
+
|
|
8
|
+
g8.init({ apiKey: process.env.G8_API_KEY! });
|
|
9
|
+
|
|
10
|
+
export async function crmSyncHealth() {
|
|
11
|
+
const syncs = await g8.api.crmSyncs.listCrmSyncs();
|
|
12
|
+
const out: Array<{ provider: string; connected: boolean; lastSyncAt?: string | null }> = [];
|
|
13
|
+
for (const row of syncs.data ?? []) {
|
|
14
|
+
const provider = row.provider as "hubspot" | "salesforce" | "pipedrive" | "zoho" | "sugarcrm";
|
|
15
|
+
const status = await g8.api.crmSyncs.getCrmStatus({ path: { provider } });
|
|
16
|
+
out.push({ provider, connected: Boolean(status.data?.connected), lastSyncAt: status.data?.last_sync_at });
|
|
17
|
+
}
|
|
18
|
+
return out;
|
|
19
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @title Push contacts to a connected CRM
|
|
3
|
+
* @when Sending graph8 contacts into HubSpot, Salesforce, Pipedrive, Zoho or SugarCRM. Tier `external`: it
|
|
4
|
+
* writes to the customer's CRM, so confirm the count with a human first. Discover valid field names with
|
|
5
|
+
* `getCrmFields` before building `records`; the result lists per-record `errors`.
|
|
6
|
+
*/
|
|
7
|
+
import { g8 } from "@graph8/sdk";
|
|
8
|
+
|
|
9
|
+
g8.init({ apiKey: process.env.G8_API_KEY! });
|
|
10
|
+
|
|
11
|
+
type Provider = "hubspot" | "salesforce" | "pipedrive" | "zoho" | "sugarcrm";
|
|
12
|
+
|
|
13
|
+
export async function pushContacts(provider: Provider, records: Array<Record<string, unknown>>, humanApproved: boolean) {
|
|
14
|
+
if (!humanApproved) throw new Error(`Refusing to push ${records.length} contacts to ${provider} without approval`);
|
|
15
|
+
const status = await g8.api.crmSyncs.getCrmStatus({ path: { provider } });
|
|
16
|
+
if (!status.data?.connected) throw new Error(`${provider} is not connected: connect it in the graph8 app`);
|
|
17
|
+
|
|
18
|
+
const result = await g8.api.crmSyncs.pushContactsToCrm({ path: { provider }, body: { records } });
|
|
19
|
+
return { pushed: result.data?.pushed_count ?? 0, failed: result.data?.failed_count ?? 0, errors: result.data?.errors ?? [] };
|
|
20
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @title Subscribe a URL to graph8 events
|
|
3
|
+
* @when Wiring a server to graph8. Tier `external` (it changes where your data is sent): get a human OK for
|
|
4
|
+
* the URL and the event list first. The signing secret is returned ONCE: store it as G8_WEBHOOK_SECRET.
|
|
5
|
+
*/
|
|
6
|
+
import { g8 } from "@graph8/sdk";
|
|
7
|
+
|
|
8
|
+
g8.init({ apiKey: process.env.G8_API_KEY! });
|
|
9
|
+
|
|
10
|
+
export async function registerWebhook(url: string, events: string[]) {
|
|
11
|
+
const available = await g8.api.webhooks.listWebhookEvents();
|
|
12
|
+
const known = new Set((available.data ?? []).map((e) => e.event));
|
|
13
|
+
const unknown = events.filter((e) => !known.has(e));
|
|
14
|
+
if (unknown.length) throw new Error(`Unknown events: ${unknown.join(", ")}`);
|
|
15
|
+
|
|
16
|
+
const created = await g8.api.webhooks.createWebhook({ body: { url, events, name: "app" } });
|
|
17
|
+
return { id: created.data?.id, secret: created.data?.secret };
|
|
18
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @title Receive graph8 webhooks in a Next.js route and verify the signature
|
|
3
|
+
* @when After WebhookManager created a subscription. Copy the body of `POST` into
|
|
4
|
+
* `app/api/webhooks/graph8/route.ts`. Verify the RAW body BEFORE parsing; reject stale timestamps; the
|
|
5
|
+
* secret lives only in `G8_WEBHOOK_SECRET` on the server. Delivery is at least once: deduplicate on the id.
|
|
6
|
+
*/
|
|
7
|
+
import { g8, WebhookSignatureError } from "@graph8/sdk";
|
|
8
|
+
|
|
9
|
+
const seen = new Set<string>();
|
|
10
|
+
|
|
11
|
+
export async function POST(request: Request): Promise<Response> {
|
|
12
|
+
const raw = await request.text();
|
|
13
|
+
try {
|
|
14
|
+
const event = g8.webhooks.constructEvent(
|
|
15
|
+
raw,
|
|
16
|
+
request.headers.get("x-studio-signature") ?? "",
|
|
17
|
+
request.headers.get("x-studio-timestamp") ?? "",
|
|
18
|
+
process.env.G8_WEBHOOK_SECRET!,
|
|
19
|
+
{ toleranceSeconds: 300 },
|
|
20
|
+
);
|
|
21
|
+
const id = String((event as { id?: unknown }).id ?? "");
|
|
22
|
+
if (id && seen.has(id)) return new Response(null, { status: 200 });
|
|
23
|
+
if (id) seen.add(id); // use a durable store in production
|
|
24
|
+
console.log("graph8 event", event.event);
|
|
25
|
+
return new Response(null, { status: 200 });
|
|
26
|
+
} catch (err) {
|
|
27
|
+
// Only a bad signature or stale timestamp is the sender's fault (400). Anything else is OUR failure:
|
|
28
|
+
// answer 5xx so graph8 retries the delivery instead of dropping it.
|
|
29
|
+
if (err instanceof WebhookSignatureError) return new Response("invalid signature", { status: 400 });
|
|
30
|
+
console.error("graph8 webhook handler failed", err);
|
|
31
|
+
return new Response("handler error", { status: 500 });
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# intent
|
|
2
|
+
|
|
3
|
+
Track the keywords and competitor sites your market researches, see which accounts show buying intent,
|
|
4
|
+
and score any account by strength and buying-committee coverage. The Apollo / 6sense "buyer intent" screen.
|
|
5
|
+
|
|
6
|
+
Depends on: `crm` (account rows link to your company records). Install: `npx @graph8/sdk add intent`.
|
|
7
|
+
|
|
8
|
+
## Components
|
|
9
|
+
|
|
10
|
+
| Component | What it does | Tiers |
|
|
11
|
+
|---|---|---|
|
|
12
|
+
| `IntentKeywordManager` | Track keywords, preview and track a competitor domain, load the companies behind a keyword, stop tracking. | billable, destructive (all confirmed) |
|
|
13
|
+
| `IntentFeed` | Accounts showing intent, strongest first. High-intent filter, keyword scope, paging, optional timed reload, "who visited this URL". | read |
|
|
14
|
+
| `AccountIntentScore` | Intent badge + committee coverage for one or many accounts. | read |
|
|
15
|
+
| `useAccountIntent` (hook) | The rollup for up to 500 company ids, for a table column. | read |
|
|
16
|
+
|
|
17
|
+
## The flow, in order
|
|
18
|
+
|
|
19
|
+
1. **Track something.** `IntentKeywordManager` -> `keywordsAdd` (plain keywords) or `keywordsCreateFromDomain`
|
|
20
|
+
with `dry_run: true` (preview), a human confirms, then `dry_run: false`. The response `keyword_id` is the
|
|
21
|
+
tracked keyword's id.
|
|
22
|
+
2. **Wait for traffic.** There is no push: new accounts appear in the feed as visitors resolve. Reload with
|
|
23
|
+
the Refresh button or `refreshSeconds`.
|
|
24
|
+
3. **Read the feed.** `IntentFeed` -> `listAbmCompanies` (`days`, `keyword_ids` from step 1, `page`, `limit`).
|
|
25
|
+
Each row's `company_id` is graph8's GLOBAL company id.
|
|
26
|
+
4. **Score a company you already hold.** `AccountIntentScore` / `useAccountIntent` -> `rollupAbmCompanies`
|
|
27
|
+
with those global `company_id`s (not your CRM row ids). Companies with no signal are absent from the result.
|
|
28
|
+
5. **Drill down.** `keywordCompanies` (billable, confirmed) lists the companies behind one keyword;
|
|
29
|
+
`urlCompanies` (free) lists who visited one page.
|
|
30
|
+
|
|
31
|
+
## Prerequisites (the human or workspace must do these)
|
|
32
|
+
|
|
33
|
+
- ABM intent enabled for the workspace (`where: "api"`): the feed and rollup answer 403 otherwise; the
|
|
34
|
+
components show a clear message instead of an empty table.
|
|
35
|
+
- Something to measure: a tracked keyword/domain, or the tracking snippet (see the `visitors` block).
|
|
36
|
+
|
|
37
|
+
## Defaults (do not ask the human)
|
|
38
|
+
|
|
39
|
+
- Window: 30 days. Page size 25 (feed) / 50 (keywords).
|
|
40
|
+
- Listing keywords is billable in the contract, so `IntentKeywordManager` asks first. Pass
|
|
41
|
+
`confirmListing={false}` only after you have confirmed your plan does not charge for it.
|
|
42
|
+
- **Ask the human** which competitor domains to track. Never invent competitors.
|
|
43
|
+
|
|
44
|
+
## Operations
|
|
45
|
+
|
|
46
|
+
| Operation | Method and path | Tier | Used by |
|
|
47
|
+
|---|---|---|---|
|
|
48
|
+
| `keywords_list_intent_keywords_list_post` | POST /intent/keywords/list | billable | IntentKeywordManager |
|
|
49
|
+
| `keywords_add_intent_keywords_add_post` | POST /intent/keywords/add | billable | IntentKeywordManager |
|
|
50
|
+
| `keywords_create_from_domain_intent_keywords_create_from_domain_post` | POST /intent/keywords/create-from-domain | billable | IntentKeywordManager |
|
|
51
|
+
| `keyword_companies_intent_keywords__keyword_id__companies_post` | POST /intent/keywords/{keyword_id}/companies | billable | IntentKeywordManager |
|
|
52
|
+
| `keywords_delete_intent_keywords__keyword_id__delete` | DELETE /intent/keywords/{keyword_id} | destructive | IntentKeywordManager |
|
|
53
|
+
| `list_abm_companies_intent_abm_companies_get` | GET /intent/abm/companies | read | IntentFeed |
|
|
54
|
+
| `url_companies_intent_url_companies_post` | POST /intent/url-companies | read | IntentFeed |
|
|
55
|
+
| `rollup_abm_companies_intent_abm_rollup_post` | POST /intent/abm/rollup | read | AccountIntentScore, useAccountIntent |
|
|
56
|
+
|
|
57
|
+
Recipes (`recipes/`): `track-competitor-domain.ts`, `rank-intent-accounts.ts`, `who-visited-url.ts`.
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "../block.schema.json",
|
|
3
|
+
"id": "intent",
|
|
4
|
+
"title": "Intent",
|
|
5
|
+
"summary": "Track keywords and competitor sites, see which accounts show buying intent, and score accounts by strength.",
|
|
6
|
+
"features": [
|
|
7
|
+
"tracked keywords and competitor domains",
|
|
8
|
+
"account intent feed with high-intent flag",
|
|
9
|
+
"account strength and buying-committee coverage",
|
|
10
|
+
"who visited this URL lookup"
|
|
11
|
+
],
|
|
12
|
+
"dependsOn": [
|
|
13
|
+
"crm"
|
|
14
|
+
],
|
|
15
|
+
"operations": [
|
|
16
|
+
"keywords_list_intent_keywords_list_post",
|
|
17
|
+
"keywords_add_intent_keywords_add_post",
|
|
18
|
+
"keywords_create_from_domain_intent_keywords_create_from_domain_post",
|
|
19
|
+
"keyword_companies_intent_keywords__keyword_id__companies_post",
|
|
20
|
+
"keywords_delete_intent_keywords__keyword_id__delete",
|
|
21
|
+
"list_abm_companies_intent_abm_companies_get",
|
|
22
|
+
"url_companies_intent_url_companies_post",
|
|
23
|
+
"rollup_abm_companies_intent_abm_rollup_post"
|
|
24
|
+
],
|
|
25
|
+
"components": [
|
|
26
|
+
{
|
|
27
|
+
"name": "IntentKeywordManager",
|
|
28
|
+
"file": "components/intent-keyword-manager.tsx",
|
|
29
|
+
"purpose": "Add, preview, list and remove tracked keywords and competitor domains; load the companies behind a keyword. Every call is confirmed.",
|
|
30
|
+
"operations": [
|
|
31
|
+
"keywords_list_intent_keywords_list_post",
|
|
32
|
+
"keywords_add_intent_keywords_add_post",
|
|
33
|
+
"keywords_create_from_domain_intent_keywords_create_from_domain_post",
|
|
34
|
+
"keyword_companies_intent_keywords__keyword_id__companies_post",
|
|
35
|
+
"keywords_delete_intent_keywords__keyword_id__delete"
|
|
36
|
+
],
|
|
37
|
+
"tiers": [
|
|
38
|
+
"billable",
|
|
39
|
+
"destructive"
|
|
40
|
+
]
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
"name": "IntentFeed",
|
|
44
|
+
"file": "components/intent-feed.tsx",
|
|
45
|
+
"purpose": "Accounts showing intent, strongest first, with a high-intent filter, keyword scoping, paging and a who-visited-this-URL lookup.",
|
|
46
|
+
"operations": [
|
|
47
|
+
"list_abm_companies_intent_abm_companies_get",
|
|
48
|
+
"url_companies_intent_url_companies_post"
|
|
49
|
+
],
|
|
50
|
+
"tiers": [
|
|
51
|
+
"read"
|
|
52
|
+
]
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
"name": "AccountIntentScore",
|
|
56
|
+
"file": "components/account-intent-score.tsx",
|
|
57
|
+
"purpose": "Compact intent badge and buying-committee coverage for one or more accounts; drop into a company table or header.",
|
|
58
|
+
"operations": [
|
|
59
|
+
"rollup_abm_companies_intent_abm_rollup_post"
|
|
60
|
+
],
|
|
61
|
+
"tiers": [
|
|
62
|
+
"read"
|
|
63
|
+
]
|
|
64
|
+
}
|
|
65
|
+
],
|
|
66
|
+
"hooks": [
|
|
67
|
+
{
|
|
68
|
+
"name": "useAccountIntent",
|
|
69
|
+
"file": "hooks/use-account-intent.ts",
|
|
70
|
+
"purpose": "Fetch the ABM intent rollup for up to 500 company ids, for table columns.",
|
|
71
|
+
"operations": [
|
|
72
|
+
"rollup_abm_companies_intent_abm_rollup_post"
|
|
73
|
+
]
|
|
74
|
+
}
|
|
75
|
+
],
|
|
76
|
+
"lib": [
|
|
77
|
+
{
|
|
78
|
+
"file": "lib/intent-utils.ts",
|
|
79
|
+
"purpose": "Row types and envelope helpers shared by the intent components."
|
|
80
|
+
}
|
|
81
|
+
],
|
|
82
|
+
"recipes": [
|
|
83
|
+
{
|
|
84
|
+
"file": "recipes/track-competitor-domain.ts",
|
|
85
|
+
"title": "Track a competitor's website and read who visits it",
|
|
86
|
+
"when": "Starting competitor intent: preview, human approval, save, then list the interested companies."
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
"file": "recipes/rank-intent-accounts.ts",
|
|
90
|
+
"title": "Rank accounts by buying intent",
|
|
91
|
+
"when": "Building a who-to-call-today list from the ABM feed and rollup."
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
"file": "recipes/who-visited-url.ts",
|
|
95
|
+
"title": "List the companies whose people visited one URL",
|
|
96
|
+
"when": "Checking interest in a single page."
|
|
97
|
+
}
|
|
98
|
+
],
|
|
99
|
+
"prerequisites": [
|
|
100
|
+
{
|
|
101
|
+
"id": "abm-intent-enabled",
|
|
102
|
+
"description": "The ABM intent feature is enabled for the graph8 workspace (the feed and rollup return 403 otherwise).",
|
|
103
|
+
"where": "api",
|
|
104
|
+
"verify": "GET /intent/abm/companies?limit=1 returns 200, not 403."
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
"id": "tracking-live",
|
|
108
|
+
"description": "Intent needs data: at least one tracked keyword or domain, or the graph8 tracking snippet on your site.",
|
|
109
|
+
"where": "api",
|
|
110
|
+
"verify": "GET /intent/stats returns non-zero total_keywords or documents."
|
|
111
|
+
}
|
|
112
|
+
],
|
|
113
|
+
"scopes": [
|
|
114
|
+
"intent:delete",
|
|
115
|
+
"intent:read",
|
|
116
|
+
"intent:run"
|
|
117
|
+
],
|
|
118
|
+
"defaults": [
|
|
119
|
+
{
|
|
120
|
+
"decision": "Look-back window for the feed and rollups",
|
|
121
|
+
"default": "30 days",
|
|
122
|
+
"askHuman": false
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
"decision": "Confirm before listing tracked keywords",
|
|
126
|
+
"default": "Yes: the list call is billable in the contract (confirmListing)",
|
|
127
|
+
"askHuman": false
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
"decision": "Which competitor domains to track",
|
|
131
|
+
"default": "None: do not guess competitors",
|
|
132
|
+
"askHuman": true
|
|
133
|
+
}
|
|
134
|
+
],
|
|
135
|
+
"peerDependencies": {
|
|
136
|
+
"react": ">=18"
|
|
137
|
+
}
|
|
138
|
+
}
|