@friggframework/core 2.0.0--canary.698.b1a2d30.0 → 2.0.0--canary.700.80ec1cc.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.
Files changed (92) hide show
  1. package/generated/prisma-mongodb/edge.js +2 -2
  2. package/generated/prisma-mongodb/index.js +2 -2
  3. package/generated/prisma-mongodb/package.json +1 -1
  4. package/generated/prisma-mongodb/schema.prisma +2 -2
  5. package/generated/prisma-mongodb/wasm.js +2 -2
  6. package/generated/prisma-postgresql/edge.js +2 -2
  7. package/generated/prisma-postgresql/index.js +2 -2
  8. package/generated/prisma-postgresql/package.json +1 -1
  9. package/generated/prisma-postgresql/schema.prisma +2 -2
  10. package/generated/prisma-postgresql/wasm.js +2 -2
  11. package/handlers/app-handler-helpers.js +5 -2
  12. package/handlers/integration-event-dispatcher.js +1 -1
  13. package/integrations/{data-defined → definitions}/README.md +12 -12
  14. package/integrations/{data-defined → definitions}/capabilities/AUDIT.md +104 -75
  15. package/integrations/{data-defined → definitions}/capabilities/asana.capabilities.json +13 -8
  16. package/integrations/{data-defined → definitions}/capabilities/fathom.capabilities.json +4 -4
  17. package/integrations/{data-defined → definitions}/capabilities/hubspot.capabilities.json +77 -49
  18. package/integrations/{data-defined → definitions}/capabilities/linear.capabilities.json +38 -20
  19. package/integrations/{data-defined → definitions}/capabilities/phone.capabilities.json +1 -2
  20. package/integrations/{data-defined → definitions}/capabilities/pipedrive.capabilities.json +8 -6
  21. package/integrations/{data-defined → definitions}/capabilities/salesforce.capabilities.json +30 -15
  22. package/integrations/{data-defined → definitions}/capabilities/slack.capabilities.json +7 -4
  23. package/integrations/{data-defined → definitions}/capabilities/stripe.capabilities.json +27 -18
  24. package/integrations/{data-defined → definitions}/config-form.js +1 -1
  25. package/integrations/{data-defined → definitions}/examples/fathom-hubspot-notion.definition.json +9 -9
  26. package/integrations/{data-defined → definitions}/examples/hubspot-slack.definition.json +25 -7
  27. package/integrations/{data-defined → definitions}/examples/phone-hubspot.definition.json +1 -1
  28. package/integrations/{data-defined → definitions}/examples/pipedrive-hubspot.definition.json +1 -1
  29. package/integrations/{data-defined → definitions}/examples/records/phone-hubspot.definition-version.json +1 -1
  30. package/integrations/{data-defined → definitions}/examples/records/phone-hubspot.integration.json +1 -1
  31. package/integrations/{data-defined → definitions}/expressions.js +1 -1
  32. package/integrations/{data-defined → definitions}/index.js +2 -2
  33. package/integrations/{data-defined/data-defined-integration.js → definitions/integration-runtime.js} +22 -21
  34. package/integrations/{data-defined → definitions}/renderers/hubspot.js +13 -8
  35. package/integrations/{data-defined → definitions}/router.js +11 -11
  36. package/integrations/{data-defined → definitions}/runtime.js +1 -1
  37. package/integrations/{data-defined → definitions}/schema/installation.v0.1.schema.json +1 -1
  38. package/integrations/{data-defined → definitions}/schema/records.md +1 -1
  39. package/integrations/{data-defined → definitions}/scripts/validate.js +1 -1
  40. package/integrations/{data-defined → definitions}/use-cases/definition-use-cases.js +1 -1
  41. package/integrations/{data-defined → definitions}/versioning.js +1 -1
  42. package/integrations/integration-base.js +31 -1
  43. package/integrations/integration-router.js +1 -1
  44. package/package.json +6 -6
  45. package/prisma-mongodb/schema.prisma +2 -2
  46. package/prisma-postgresql/schema.prisma +2 -2
  47. /package/integrations/{data-defined → definitions}/canonical.js +0 -0
  48. /package/integrations/{data-defined → definitions}/capabilities/acmeerp.capabilities.json +0 -0
  49. /package/integrations/{data-defined → definitions}/capabilities/calendly.capabilities.json +0 -0
  50. /package/integrations/{data-defined → definitions}/capabilities/docusign.capabilities.json +0 -0
  51. /package/integrations/{data-defined → definitions}/capabilities/github.capabilities.json +0 -0
  52. /package/integrations/{data-defined → definitions}/capabilities/jira.capabilities.json +0 -0
  53. /package/integrations/{data-defined → definitions}/capabilities/klaviyo.capabilities.json +0 -0
  54. /package/integrations/{data-defined → definitions}/capabilities/netsuite.capabilities.json +0 -0
  55. /package/integrations/{data-defined → definitions}/capabilities/notion.capabilities.json +0 -0
  56. /package/integrations/{data-defined → definitions}/capabilities/quickbooks.capabilities.json +0 -0
  57. /package/integrations/{data-defined → definitions}/capabilities/shopify.capabilities.json +0 -0
  58. /package/integrations/{data-defined → definitions}/capabilities/webflow.capabilities.json +0 -0
  59. /package/integrations/{data-defined → definitions}/capabilities/zendesk.capabilities.json +0 -0
  60. /package/integrations/{data-defined → definitions}/compile.js +0 -0
  61. /package/integrations/{data-defined → definitions}/examples/acme-erp-quickbooks.definition.json +0 -0
  62. /package/integrations/{data-defined → definitions}/examples/asana-slack.definition.json +0 -0
  63. /package/integrations/{data-defined → definitions}/examples/calendly-salesforce.definition.json +0 -0
  64. /package/integrations/{data-defined → definitions}/examples/docusign-salesforce.definition.json +0 -0
  65. /package/integrations/{data-defined → definitions}/examples/github-linear.definition.json +0 -0
  66. /package/integrations/{data-defined → definitions}/examples/klaviyo-hubspot.definition.json +0 -0
  67. /package/integrations/{data-defined → definitions}/examples/pipedrive-phone.definition.json +0 -0
  68. /package/integrations/{data-defined → definitions}/examples/records/phone-hubspot.mappings.json +0 -0
  69. /package/integrations/{data-defined → definitions}/examples/records/phone-hubspot.workflow-run.json +0 -0
  70. /package/integrations/{data-defined → definitions}/examples/shopify-klaviyo.definition.json +0 -0
  71. /package/integrations/{data-defined → definitions}/examples/shopify-netsuite.definition.json +0 -0
  72. /package/integrations/{data-defined → definitions}/examples/stripe-quickbooks.definition.json +0 -0
  73. /package/integrations/{data-defined → definitions}/examples/webflow-hubspot-slack.definition.json +0 -0
  74. /package/integrations/{data-defined → definitions}/examples/zendesk-jira.definition.json +0 -0
  75. /package/integrations/{data-defined → definitions}/interpreter.js +0 -0
  76. /package/integrations/{data-defined → definitions}/manifests.js +0 -0
  77. /package/integrations/{data-defined → definitions}/renderers/common.js +0 -0
  78. /package/integrations/{data-defined → definitions}/renderers/html.js +0 -0
  79. /package/integrations/{data-defined → definitions}/renderers/preview.js +0 -0
  80. /package/integrations/{data-defined → definitions}/renderers/slack.js +0 -0
  81. /package/integrations/{data-defined → definitions}/repositories/definition-version-repository.js +0 -0
  82. /package/integrations/{data-defined → definitions}/repositories/workflow-run-repository.js +0 -0
  83. /package/integrations/{data-defined → definitions}/schema/capability-manifest.v0.1.schema.json +0 -0
  84. /package/integrations/{data-defined → definitions}/schema/integration-definition.v0.1.schema.json +0 -0
  85. /package/integrations/{data-defined → definitions}/schema-check.js +0 -0
  86. /package/integrations/{data-defined → definitions}/scripts/check-manifests.js +0 -0
  87. /package/integrations/{data-defined → definitions}/steps.js +0 -0
  88. /package/integrations/{data-defined → definitions}/sync-engine.js +0 -0
  89. /package/integrations/{data-defined → definitions}/tests/helpers-fake-crm.js +0 -0
  90. /package/integrations/{data-defined → definitions}/transforms.js +0 -0
  91. /package/integrations/{data-defined → definitions}/validate.js +0 -0
  92. /package/integrations/{data-defined → definitions}/webhooks.js +0 -0
@@ -428,8 +428,8 @@ const config = {
428
428
  }
429
429
  }
430
430
  },
431
- "inlineSchema": "// Frigg Framework - Prisma Schema (PostgreSQL)\n// PostgreSQL database schema for enterprise integration platform\n// Converted from MongoDB schema for relational database support\n\ngenerator client {\n provider = \"prisma-client-js\"\n output = \"../generated/prisma-postgresql\"\n binaryTargets = [\"native\", \"rhel-openssl-3.0.x\"] // native for local dev, rhel for Lambda deployment\n // Library engine (default since Prisma 3.x): Rust query engine loads as a\n // Node-API addon inside the same process. The binary engine forks a child\n // query-engine subprocess and communicates over a local HTTP/IPC pipe with\n // NO client-side timeout — a zombied child wedges the Node process until\n // Lambda's 900s cap. Switching to library eliminates that entire class of\n // silent hangs. See friggframework/frigg#580 for the investigation.\n engineType = \"library\"\n}\n\ndatasource db {\n provider = \"postgresql\"\n url = env(\"DATABASE_URL\")\n}\n\n// ============================================================================\n// USER MODELS\n// ============================================================================\n\n/// User model with discriminator pattern support\n/// Replaces Mongoose discriminators (IndividualUser, OrganizationUser)\nmodel User {\n id Int @id @default(autoincrement())\n type UserType\n\n // Timestamps\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n // IndividualUser fields (nullable for organizations)\n email String?\n username String?\n hashword String? // Bcrypt hashed password (handled in application layer)\n appUserId String?\n organizationId Int?\n\n // Self-referential relation for organization membership\n organization User? @relation(\"OrgMembers\", fields: [organizationId], references: [id], onDelete: NoAction, onUpdate: NoAction)\n members User[] @relation(\"OrgMembers\")\n\n // OrganizationUser fields (nullable for individuals)\n appOrgId String?\n name String?\n\n // Relations\n tokens Token[]\n credentials Credential[]\n entities Entity[]\n integrations Integration[]\n processes Process[]\n\n @@unique([username, appUserId])\n @@index([type])\n @@index([appUserId])\n @@index([organizationId])\n}\n\nenum UserType {\n INDIVIDUAL\n ORGANIZATION\n}\n\n// ============================================================================\n// AUTHENTICATION MODELS\n// ============================================================================\n\n/// Authentication tokens with expiration\n/// Bcrypt hashed tokens stored (handled in application layer)\nmodel Token {\n id Int @id @default(autoincrement())\n token String // Bcrypt hashed\n created DateTime @default(now())\n expires DateTime?\n userId Int\n user User @relation(fields: [userId], references: [id], onDelete: Cascade)\n\n @@index([userId])\n @@index([expires])\n}\n\n// ============================================================================\n// CREDENTIAL & ENTITY MODELS\n// ============================================================================\n\n/// OAuth credentials and API tokens\n/// All sensitive data encrypted with KMS at rest\nmodel Credential {\n id Int @id @default(autoincrement())\n userId Int?\n user User? @relation(fields: [userId], references: [id], onDelete: Cascade)\n authIsValid Boolean?\n externalId String?\n\n // Dynamic OAuth fields stored as JSON (encrypted via Prisma middleware)\n // Contains: access_token, refresh_token, domain, expires_in, token_type, etc.\n data Json @default(\"{}\")\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n // Relations\n entities Entity[]\n\n @@index([userId])\n @@index([externalId])\n}\n\n/// External service entities (API connections)\nmodel Entity {\n id Int @id @default(autoincrement())\n credentialId Int?\n credential Credential? @relation(fields: [credentialId], references: [id], onDelete: SetNull)\n userId Int?\n user User? @relation(fields: [userId], references: [id], onDelete: Cascade)\n name String?\n moduleName String?\n externalId String?\n\n data Json @default(\"{}\")\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n // Relations - many-to-many with implicit join tables\n integrations Integration[]\n syncs Sync[]\n\n dataIdentifiers DataIdentifier[]\n associationObjects AssociationObject[]\n\n @@index([userId])\n @@index([externalId])\n @@index([moduleName])\n @@index([credentialId])\n}\n\n// ============================================================================\n// INTEGRATION MODELS\n// ============================================================================\n\n/// Main integration configuration and state\nmodel Integration {\n id Int @id @default(autoincrement())\n userId Int?\n user User? @relation(fields: [userId], references: [id], onDelete: Cascade)\n status IntegrationStatus @default(IN_CREATION)\n\n // Configuration and version\n config Json? // Integration configuration object\n version String?\n\n // Entity references (many-to-many via implicit join table)\n entities Entity[]\n\n // Message arrays (stored as JSON)\n errors Json @default(\"[]\")\n warnings Json @default(\"[]\")\n info Json @default(\"[]\")\n logs Json @default(\"[]\")\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n // Relations\n associations Association[]\n syncs Sync[]\n mappings IntegrationMapping[]\n processes Process[]\n workflowRuns WorkflowRun[]\n\n @@index([userId])\n @@index([status])\n}\n\nenum IntegrationStatus {\n ENABLED\n NEEDS_CONFIG\n PROCESSING\n DISABLED\n ERROR\n IN_CREATION\n IN_DELETION\n}\n\n/// Integration-specific data mappings\n/// All mapping data encrypted with KMS\nmodel IntegrationMapping {\n id Int @id @default(autoincrement())\n integrationId Int\n integration Integration @relation(fields: [integrationId], references: [id], onDelete: Cascade)\n sourceId String?\n\n // Encrypted mapping data (handled via Prisma middleware)\n mapping Json?\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n @@unique([integrationId, sourceId])\n @@index([integrationId])\n @@index([sourceId])\n}\n\n// ============================================================================\n// SYNC MODELS\n// ============================================================================\n\n/// Bidirectional data synchronization tracking\nmodel Sync {\n id Int @id @default(autoincrement())\n integrationId Int?\n integration Integration? @relation(fields: [integrationId], references: [id], onDelete: Cascade)\n\n // Entity references (many-to-many via implicit join table)\n entities Entity[]\n\n hash String\n name String\n\n // Data identifiers (extracted to separate model)\n dataIdentifiers DataIdentifier[]\n\n @@index([integrationId])\n @@index([hash])\n @@index([name])\n}\n\n/// Data identifier for sync operations\n/// Replaces nested array structure in Mongoose\nmodel DataIdentifier {\n id Int @id @default(autoincrement())\n syncId Int?\n sync Sync? @relation(fields: [syncId], references: [id], onDelete: Cascade)\n entityId Int\n entity Entity @relation(fields: [entityId], references: [id], onDelete: Cascade)\n\n // Identifier data (can be any structure)\n idData Json\n\n hash String\n\n @@index([syncId])\n @@index([entityId])\n @@index([hash])\n}\n\n// ============================================================================\n// ASSOCIATION MODELS\n// ============================================================================\n\n/// Entity associations with cardinality tracking\nmodel Association {\n id Int @id @default(autoincrement())\n integrationId Int\n integration Integration @relation(fields: [integrationId], references: [id], onDelete: Cascade)\n name String\n type AssociationType\n primaryObject String\n\n // Associated objects (extracted to separate model)\n objects AssociationObject[]\n\n @@index([integrationId])\n @@index([name])\n}\n\n/// Association object entry\n/// Replaces nested array structure in Mongoose\nmodel AssociationObject {\n id Int @id @default(autoincrement())\n associationId Int\n association Association @relation(fields: [associationId], references: [id], onDelete: Cascade)\n entityId Int\n entity Entity @relation(fields: [entityId], references: [id], onDelete: Cascade)\n objectType String\n objId String\n metadata Json? // Optional metadata\n\n @@index([associationId])\n @@index([entityId])\n}\n\nenum AssociationType {\n ONE_TO_MANY\n ONE_TO_ONE\n MANY_TO_ONE\n}\n\n// ============================================================================\n// PROCESS MODELS\n// ============================================================================\n\n/// Generic Process Model - tracks any long-running operation\n/// Used for: CRM syncs, data migrations, bulk operations, etc.\nmodel Process {\n id Int @id @default(autoincrement())\n\n // Core references\n userId Int\n user User @relation(fields: [userId], references: [id], onDelete: Cascade)\n integrationId Int\n integration Integration @relation(fields: [integrationId], references: [id], onDelete: Cascade)\n\n // Process identification\n name String // e.g., \"zoho-crm-contact-sync\", \"pipedrive-lead-sync\"\n type String // e.g., \"CRM_SYNC\", \"DATA_MIGRATION\", \"BULK_OPERATION\"\n\n // State machine\n state String // Current state (integration-defined states)\n\n // Flexible storage\n context Json @default(\"{}\") // Process-specific data (pagination, metadata, etc.)\n results Json @default(\"{}\") // Process results and metrics\n\n // Hierarchy support - self-referential relation\n parentProcessId Int?\n parentProcess Process? @relation(\"ProcessHierarchy\", fields: [parentProcessId], references: [id], onDelete: SetNull)\n childProcesses Process[] @relation(\"ProcessHierarchy\")\n\n // Timestamps\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n @@index([userId])\n @@index([integrationId])\n @@index([type])\n @@index([state])\n @@index([name])\n @@index([parentProcessId])\n}\n\n// ============================================================================\n// UTILITY MODELS\n// ============================================================================\n\n/// Generic state storage\nmodel State {\n id Int @id @default(autoincrement())\n state Json?\n}\n\n/// AWS API Gateway WebSocket connection tracking\nmodel WebsocketConnection {\n id Int @id @default(autoincrement())\n connectionId String?\n\n @@index([connectionId])\n}\n\n// ============================================================================\n// ADMIN PROCESS MODELS\n// ============================================================================\n\n/// Admin process state machine\nenum AdminScriptExecutionState {\n PENDING\n RUNNING\n COMPLETED\n FAILED\n}\n\n/// Admin process tracking (like Process but without user/integration FK)\n/// Used for: admin scripts, db migrations, system maintenance tasks\nmodel AdminScriptExecution {\n id Int @id @default(autoincrement())\n name String // e.g., \"oauth-token-refresh\", \"db-migration-xyz\"\n type String // e.g., \"ADMIN_SCRIPT\", \"DB_MIGRATION\"\n\n // State machine\n state AdminScriptExecutionState @default(PENDING)\n\n // Flexible storage (mirrors Process model pattern)\n context Json @default(\"{}\") // input, trigger, audit info, script version\n results Json @default(\"{}\") // output, logs, metrics, errors\n\n // Hierarchy support - self-referential relation\n parentExecutionId Int?\n parentExecution AdminScriptExecution? @relation(\"AdminScriptExecutionHierarchy\", fields: [parentExecutionId], references: [id], onDelete: SetNull)\n childExecutions AdminScriptExecution[] @relation(\"AdminScriptExecutionHierarchy\")\n\n // Timestamps\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n @@index([name, createdAt(sort: Desc)])\n @@index([state])\n @@index([type])\n}\n\n/// Script scheduling configuration for hybrid scheduling (SQS + EventBridge)\nmodel ScriptSchedule {\n id Int @id @default(autoincrement())\n scriptName String @unique\n enabled Boolean @default(false)\n cronExpression String?\n timezone String @default(\"UTC\")\n lastTriggeredAt DateTime?\n nextTriggerAt DateTime?\n\n // External Scheduler (e.g., AWS EventBridge Scheduler)\n externalScheduleId String?\n externalScheduleName String?\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n @@index([enabled])\n}\n\n/// Durable usage counters, deliberately isolated: no userId and no foreign key\n/// to Integration, so a user-scoped query can never return a usage row and usage\n/// history survives integration deletion.\nmodel UsageCounter {\n id Int @id @default(autoincrement())\n\n integrationId String\n integrationType String\n metric String\n window String // e.g. \"day:2026-07-05\" or \"hour:2026-07-05T14\"\n value BigInt @default(0) // BigInt: a busy day/hour counter can exceed 32-bit\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n @@unique([integrationId, integrationType, metric, window])\n // Covers totals() (metric + window prefix/range, no integrationType filter).\n @@index([metric, window])\n // Covers series() (metric + integrationType + window range).\n @@index([metric, integrationType, window])\n}\n\n// Data-defined integrations (ADR-063): one immutable row per published definition version.\nenum DefinitionStatus {\n DRAFT\n PUBLISHED\n DEPRECATED\n RETIRED\n}\n\nmodel IntegrationDefinitionVersion {\n id Int @id @default(autoincrement())\n definitionId String\n version String\n schemaVersion String\n status DefinitionStatus @default(DRAFT)\n checksum String\n document Json\n changeLevel String?\n previousVersion String?\n notes String?\n createdBy String?\n // The workspace an integration a user built belongs to (null: the app's own catalogue, offered to everyone).\n workspaceId String?\n publishedAt DateTime?\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n @@unique([definitionId, version])\n @@index([definitionId, status])\n @@index([workspaceId, status])\n}\n\n// One row per data-defined workflow run. trace holds step ids, statuses, timings and error messages, never request or\n// response bodies.\nmodel WorkflowRun {\n id Int @id @default(autoincrement())\n integrationId Int\n integration Integration @relation(fields: [integrationId], references: [id], onDelete: Cascade)\n definitionId String\n definitionVersion String\n workflowId String\n triggerType String\n eventId String?\n status String\n reason String?\n error String?\n retryable Boolean @default(false)\n trace Json @default(\"[]\")\n /// The runtime's run id (run_...): how routes and resume jobs address a run\n runKey String?\n /// A paused run (status waiting): which step, and what it waits for. Never request or response bodies.\n waitingFor Json?\n resumeAt DateTime?\n startedAt DateTime\n endedAt DateTime?\n createdAt DateTime @default(now())\n\n @@index([integrationId, runKey])\n @@index([integrationId, startedAt])\n @@index([integrationId, workflowId, startedAt])\n @@index([integrationId, status])\n}\n",
432
- "inlineSchemaHash": "7cbcf3d54afbe39160d69f8b640da30304af16dd1eb4ce3267842820b464be7a",
431
+ "inlineSchema": "// Frigg Framework - Prisma Schema (PostgreSQL)\n// PostgreSQL database schema for enterprise integration platform\n// Converted from MongoDB schema for relational database support\n\ngenerator client {\n provider = \"prisma-client-js\"\n output = \"../generated/prisma-postgresql\"\n binaryTargets = [\"native\", \"rhel-openssl-3.0.x\"] // native for local dev, rhel for Lambda deployment\n // Library engine (default since Prisma 3.x): Rust query engine loads as a\n // Node-API addon inside the same process. The binary engine forks a child\n // query-engine subprocess and communicates over a local HTTP/IPC pipe with\n // NO client-side timeout — a zombied child wedges the Node process until\n // Lambda's 900s cap. Switching to library eliminates that entire class of\n // silent hangs. See friggframework/frigg#580 for the investigation.\n engineType = \"library\"\n}\n\ndatasource db {\n provider = \"postgresql\"\n url = env(\"DATABASE_URL\")\n}\n\n// ============================================================================\n// USER MODELS\n// ============================================================================\n\n/// User model with discriminator pattern support\n/// Replaces Mongoose discriminators (IndividualUser, OrganizationUser)\nmodel User {\n id Int @id @default(autoincrement())\n type UserType\n\n // Timestamps\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n // IndividualUser fields (nullable for organizations)\n email String?\n username String?\n hashword String? // Bcrypt hashed password (handled in application layer)\n appUserId String?\n organizationId Int?\n\n // Self-referential relation for organization membership\n organization User? @relation(\"OrgMembers\", fields: [organizationId], references: [id], onDelete: NoAction, onUpdate: NoAction)\n members User[] @relation(\"OrgMembers\")\n\n // OrganizationUser fields (nullable for individuals)\n appOrgId String?\n name String?\n\n // Relations\n tokens Token[]\n credentials Credential[]\n entities Entity[]\n integrations Integration[]\n processes Process[]\n\n @@unique([username, appUserId])\n @@index([type])\n @@index([appUserId])\n @@index([organizationId])\n}\n\nenum UserType {\n INDIVIDUAL\n ORGANIZATION\n}\n\n// ============================================================================\n// AUTHENTICATION MODELS\n// ============================================================================\n\n/// Authentication tokens with expiration\n/// Bcrypt hashed tokens stored (handled in application layer)\nmodel Token {\n id Int @id @default(autoincrement())\n token String // Bcrypt hashed\n created DateTime @default(now())\n expires DateTime?\n userId Int\n user User @relation(fields: [userId], references: [id], onDelete: Cascade)\n\n @@index([userId])\n @@index([expires])\n}\n\n// ============================================================================\n// CREDENTIAL & ENTITY MODELS\n// ============================================================================\n\n/// OAuth credentials and API tokens\n/// All sensitive data encrypted with KMS at rest\nmodel Credential {\n id Int @id @default(autoincrement())\n userId Int?\n user User? @relation(fields: [userId], references: [id], onDelete: Cascade)\n authIsValid Boolean?\n externalId String?\n\n // Dynamic OAuth fields stored as JSON (encrypted via Prisma middleware)\n // Contains: access_token, refresh_token, domain, expires_in, token_type, etc.\n data Json @default(\"{}\")\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n // Relations\n entities Entity[]\n\n @@index([userId])\n @@index([externalId])\n}\n\n/// External service entities (API connections)\nmodel Entity {\n id Int @id @default(autoincrement())\n credentialId Int?\n credential Credential? @relation(fields: [credentialId], references: [id], onDelete: SetNull)\n userId Int?\n user User? @relation(fields: [userId], references: [id], onDelete: Cascade)\n name String?\n moduleName String?\n externalId String?\n\n data Json @default(\"{}\")\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n // Relations - many-to-many with implicit join tables\n integrations Integration[]\n syncs Sync[]\n\n dataIdentifiers DataIdentifier[]\n associationObjects AssociationObject[]\n\n @@index([userId])\n @@index([externalId])\n @@index([moduleName])\n @@index([credentialId])\n}\n\n// ============================================================================\n// INTEGRATION MODELS\n// ============================================================================\n\n/// Main integration configuration and state\nmodel Integration {\n id Int @id @default(autoincrement())\n userId Int?\n user User? @relation(fields: [userId], references: [id], onDelete: Cascade)\n status IntegrationStatus @default(IN_CREATION)\n\n // Configuration and version\n config Json? // Integration configuration object\n version String?\n\n // Entity references (many-to-many via implicit join table)\n entities Entity[]\n\n // Message arrays (stored as JSON)\n errors Json @default(\"[]\")\n warnings Json @default(\"[]\")\n info Json @default(\"[]\")\n logs Json @default(\"[]\")\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n // Relations\n associations Association[]\n syncs Sync[]\n mappings IntegrationMapping[]\n processes Process[]\n workflowRuns WorkflowRun[]\n\n @@index([userId])\n @@index([status])\n}\n\nenum IntegrationStatus {\n ENABLED\n NEEDS_CONFIG\n PROCESSING\n DISABLED\n ERROR\n IN_CREATION\n IN_DELETION\n}\n\n/// Integration-specific data mappings\n/// All mapping data encrypted with KMS\nmodel IntegrationMapping {\n id Int @id @default(autoincrement())\n integrationId Int\n integration Integration @relation(fields: [integrationId], references: [id], onDelete: Cascade)\n sourceId String?\n\n // Encrypted mapping data (handled via Prisma middleware)\n mapping Json?\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n @@unique([integrationId, sourceId])\n @@index([integrationId])\n @@index([sourceId])\n}\n\n// ============================================================================\n// SYNC MODELS\n// ============================================================================\n\n/// Bidirectional data synchronization tracking\nmodel Sync {\n id Int @id @default(autoincrement())\n integrationId Int?\n integration Integration? @relation(fields: [integrationId], references: [id], onDelete: Cascade)\n\n // Entity references (many-to-many via implicit join table)\n entities Entity[]\n\n hash String\n name String\n\n // Data identifiers (extracted to separate model)\n dataIdentifiers DataIdentifier[]\n\n @@index([integrationId])\n @@index([hash])\n @@index([name])\n}\n\n/// Data identifier for sync operations\n/// Replaces nested array structure in Mongoose\nmodel DataIdentifier {\n id Int @id @default(autoincrement())\n syncId Int?\n sync Sync? @relation(fields: [syncId], references: [id], onDelete: Cascade)\n entityId Int\n entity Entity @relation(fields: [entityId], references: [id], onDelete: Cascade)\n\n // Identifier data (can be any structure)\n idData Json\n\n hash String\n\n @@index([syncId])\n @@index([entityId])\n @@index([hash])\n}\n\n// ============================================================================\n// ASSOCIATION MODELS\n// ============================================================================\n\n/// Entity associations with cardinality tracking\nmodel Association {\n id Int @id @default(autoincrement())\n integrationId Int\n integration Integration @relation(fields: [integrationId], references: [id], onDelete: Cascade)\n name String\n type AssociationType\n primaryObject String\n\n // Associated objects (extracted to separate model)\n objects AssociationObject[]\n\n @@index([integrationId])\n @@index([name])\n}\n\n/// Association object entry\n/// Replaces nested array structure in Mongoose\nmodel AssociationObject {\n id Int @id @default(autoincrement())\n associationId Int\n association Association @relation(fields: [associationId], references: [id], onDelete: Cascade)\n entityId Int\n entity Entity @relation(fields: [entityId], references: [id], onDelete: Cascade)\n objectType String\n objId String\n metadata Json? // Optional metadata\n\n @@index([associationId])\n @@index([entityId])\n}\n\nenum AssociationType {\n ONE_TO_MANY\n ONE_TO_ONE\n MANY_TO_ONE\n}\n\n// ============================================================================\n// PROCESS MODELS\n// ============================================================================\n\n/// Generic Process Model - tracks any long-running operation\n/// Used for: CRM syncs, data migrations, bulk operations, etc.\nmodel Process {\n id Int @id @default(autoincrement())\n\n // Core references\n userId Int\n user User @relation(fields: [userId], references: [id], onDelete: Cascade)\n integrationId Int\n integration Integration @relation(fields: [integrationId], references: [id], onDelete: Cascade)\n\n // Process identification\n name String // e.g., \"zoho-crm-contact-sync\", \"pipedrive-lead-sync\"\n type String // e.g., \"CRM_SYNC\", \"DATA_MIGRATION\", \"BULK_OPERATION\"\n\n // State machine\n state String // Current state (integration-defined states)\n\n // Flexible storage\n context Json @default(\"{}\") // Process-specific data (pagination, metadata, etc.)\n results Json @default(\"{}\") // Process results and metrics\n\n // Hierarchy support - self-referential relation\n parentProcessId Int?\n parentProcess Process? @relation(\"ProcessHierarchy\", fields: [parentProcessId], references: [id], onDelete: SetNull)\n childProcesses Process[] @relation(\"ProcessHierarchy\")\n\n // Timestamps\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n @@index([userId])\n @@index([integrationId])\n @@index([type])\n @@index([state])\n @@index([name])\n @@index([parentProcessId])\n}\n\n// ============================================================================\n// UTILITY MODELS\n// ============================================================================\n\n/// Generic state storage\nmodel State {\n id Int @id @default(autoincrement())\n state Json?\n}\n\n/// AWS API Gateway WebSocket connection tracking\nmodel WebsocketConnection {\n id Int @id @default(autoincrement())\n connectionId String?\n\n @@index([connectionId])\n}\n\n// ============================================================================\n// ADMIN PROCESS MODELS\n// ============================================================================\n\n/// Admin process state machine\nenum AdminScriptExecutionState {\n PENDING\n RUNNING\n COMPLETED\n FAILED\n}\n\n/// Admin process tracking (like Process but without user/integration FK)\n/// Used for: admin scripts, db migrations, system maintenance tasks\nmodel AdminScriptExecution {\n id Int @id @default(autoincrement())\n name String // e.g., \"oauth-token-refresh\", \"db-migration-xyz\"\n type String // e.g., \"ADMIN_SCRIPT\", \"DB_MIGRATION\"\n\n // State machine\n state AdminScriptExecutionState @default(PENDING)\n\n // Flexible storage (mirrors Process model pattern)\n context Json @default(\"{}\") // input, trigger, audit info, script version\n results Json @default(\"{}\") // output, logs, metrics, errors\n\n // Hierarchy support - self-referential relation\n parentExecutionId Int?\n parentExecution AdminScriptExecution? @relation(\"AdminScriptExecutionHierarchy\", fields: [parentExecutionId], references: [id], onDelete: SetNull)\n childExecutions AdminScriptExecution[] @relation(\"AdminScriptExecutionHierarchy\")\n\n // Timestamps\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n @@index([name, createdAt(sort: Desc)])\n @@index([state])\n @@index([type])\n}\n\n/// Script scheduling configuration for hybrid scheduling (SQS + EventBridge)\nmodel ScriptSchedule {\n id Int @id @default(autoincrement())\n scriptName String @unique\n enabled Boolean @default(false)\n cronExpression String?\n timezone String @default(\"UTC\")\n lastTriggeredAt DateTime?\n nextTriggerAt DateTime?\n\n // External Scheduler (e.g., AWS EventBridge Scheduler)\n externalScheduleId String?\n externalScheduleName String?\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n @@index([enabled])\n}\n\n/// Durable usage counters, deliberately isolated: no userId and no foreign key\n/// to Integration, so a user-scoped query can never return a usage row and usage\n/// history survives integration deletion.\nmodel UsageCounter {\n id Int @id @default(autoincrement())\n\n integrationId String\n integrationType String\n metric String\n window String // e.g. \"day:2026-07-05\" or \"hour:2026-07-05T14\"\n value BigInt @default(0) // BigInt: a busy day/hour counter can exceed 32-bit\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n @@unique([integrationId, integrationType, metric, window])\n // Covers totals() (metric + window prefix/range, no integrationType filter).\n @@index([metric, window])\n // Covers series() (metric + integrationType + window range).\n @@index([metric, integrationType, window])\n}\n\n// Integrations published as definitions (ADR-063): one immutable row per published definition version.\nenum DefinitionStatus {\n DRAFT\n PUBLISHED\n DEPRECATED\n RETIRED\n}\n\nmodel IntegrationDefinitionVersion {\n id Int @id @default(autoincrement())\n definitionId String\n version String\n schemaVersion String\n status DefinitionStatus @default(DRAFT)\n checksum String\n document Json\n changeLevel String?\n previousVersion String?\n notes String?\n createdBy String?\n // The workspace an integration a user built belongs to (null: the app's own catalogue, offered to everyone).\n workspaceId String?\n publishedAt DateTime?\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n @@unique([definitionId, version])\n @@index([definitionId, status])\n @@index([workspaceId, status])\n}\n\n// One row per workflow run. trace holds step ids, statuses, timings and error messages, never request or\n// response bodies.\nmodel WorkflowRun {\n id Int @id @default(autoincrement())\n integrationId Int\n integration Integration @relation(fields: [integrationId], references: [id], onDelete: Cascade)\n definitionId String\n definitionVersion String\n workflowId String\n triggerType String\n eventId String?\n status String\n reason String?\n error String?\n retryable Boolean @default(false)\n trace Json @default(\"[]\")\n /// The runtime's run id (run_...): how routes and resume jobs address a run\n runKey String?\n /// A paused run (status waiting): which step, and what it waits for. Never request or response bodies.\n waitingFor Json?\n resumeAt DateTime?\n startedAt DateTime\n endedAt DateTime?\n createdAt DateTime @default(now())\n\n @@index([integrationId, runKey])\n @@index([integrationId, startedAt])\n @@index([integrationId, workflowId, startedAt])\n @@index([integrationId, status])\n}\n",
432
+ "inlineSchemaHash": "5d11039694b44af001971b7239ab8290b642c385239660aee17e709d4fa0b509",
433
433
  "copyEngine": true
434
434
  }
435
435
  config.dirname = '/'
@@ -12,8 +12,11 @@ const log = getLogger('frigg.http');
12
12
  const createApp = (applyMiddleware) => {
13
13
  const app = express();
14
14
 
15
- app.use(bodyParser.json({ limit: '10mb' }));
16
- app.use(bodyParser.urlencoded({ extended: true }));
15
+ // Keep the bytes as received: webhook signatures (HubSpot, Slack, Stripe, Linear…) are computed over the exact
16
+ // raw body, which re-serialising the parsed body does not reproduce. A reference to the buffer, not a copy.
17
+ const keepRaw = (req, res, buf) => { req.rawBody = buf; };
18
+ app.use(bodyParser.json({ limit: '10mb', verify: keepRaw }));
19
+ app.use(bodyParser.urlencoded({ extended: true, verify: keepRaw }));
17
20
  app.use(
18
21
  cors({
19
22
  origin: '*',
@@ -21,7 +21,7 @@ class IntegrationEventDispatcher {
21
21
  async dispatchJob({ event, data, context, delivery }) {
22
22
  return this._dispatch(event, (instance, handler) => {
23
23
  const invoke = () => handler.call(instance, { data, context, delivery });
24
- // An integration that keeps run history records each queued event as a run (data-defined runtime).
24
+ // An integration that keeps run history records each queued event as a run (integration runtime).
25
25
  return typeof instance.runEvent === 'function'
26
26
  ? instance.runEvent(event, invoke)
27
27
  : invoke();
@@ -1,4 +1,4 @@
1
- # Data-defined integrations (v0.1)
1
+ # Integrations published as definitions (v0.1)
2
2
 
3
3
  ADR-063, implemented to the point where a definition can be validated, compiled into Frigg events, executed, rendered
4
4
  and versioned. An integration becomes a **document** (what it connects, what it syncs, which workflows and UI it has)
@@ -17,7 +17,7 @@ installation (Integration.config) ─┘ │
17
17
  |---|---|
18
18
  | `schema/integration-definition.v0.1.schema.json` | The definition format (JSON Schema 2020-12) |
19
19
  | `schema/capability-manifest.v0.1.schema.json` | What an API module declares it can do |
20
- | `schema/installation.v0.1.schema.json` | `Integration.config` for a data-defined installation |
20
+ | `schema/installation.v0.1.schema.json` | `Integration.config` for an installation |
21
21
  | `schema/records.md` | Database records: definition versions, installation, mappings, workflow runs |
22
22
  | `capabilities/*.capabilities.json` | Capability manifests. Nine are checked against the real Api classes (`capabilities/AUDIT.md`); the other twelve are still mock-shaped |
23
23
  | `scripts/check-manifests.js` | Checks every manifest `method` exists on the real module's Api class, with the arguments it takes |
@@ -30,7 +30,7 @@ installation (Integration.config) ─┘ │
30
30
  | `sync-engine.js` | Syncs: link → identity → search → create/update, master rules, backfill pages, polls |
31
31
  | `runtime.js` | Ports (Api calls, link store), `runWorkflow`, `runQuery`, `renderSurface`, `runLifecycle`, `resolveOptions`, `installContext` |
32
32
  | `versioning.js` | `diff`, `checkBump`, `planUpgrade` |
33
- | `data-defined-integration.js` | `defineIntegrationClass`: the IntegrationBase subclass that runs it all |
33
+ | `integration-runtime.js` | `defineIntegrationClass`: the IntegrationBase subclass that runs it all |
34
34
  | `use-cases/definition-use-cases.js` | Publish, deprecate, catalogue, history, upgrade plan; `createDefinitionCatalog()` (the store the runtime reads) |
35
35
  | `repositories/` | `IntegrationDefinitionVersion` and `WorkflowRun` rows (MongoDB, PostgreSQL, DocumentDB) |
36
36
  | `router.js` | The publish, catalogue, upgrade and run-history routes |
@@ -107,9 +107,9 @@ Definitions live in the `IntegrationDefinitionVersion` table (Prisma models in b
107
107
  `20261018000000_add_data_defined_integrations`), and runs in `WorkflowRun`. Point the class at the store:
108
108
 
109
109
  ```js
110
- const { defineSharedIntegrationClass, createDefinitionCatalog } = require('@friggframework/core/integrations/data-defined');
110
+ const { defineSharedIntegrationClass, createDefinitionCatalog } = require('@friggframework/core/integrations/definitions');
111
111
  // A database store is read asynchronously, so it goes with the shared class (one class, every definition).
112
- const DataDefined = defineSharedIntegrationClass({ definitions: createDefinitionCatalog(), manifests, modules });
112
+ const IntegrationRuntime = defineSharedIntegrationClass({ definitions: createDefinitionCatalog(), manifests, modules });
113
113
  ```
114
114
 
115
115
  | Route | Auth | |
@@ -118,8 +118,8 @@ const DataDefined = defineSharedIntegrationClass({ definitions: createDefinition
118
118
  | `GET /admin/integrations/:id/versions` | admin key | Version history |
119
119
  | `POST /admin/integrations/:id/versions/:v/deprecate` | admin key | Stop new installs of a version |
120
120
  | `GET /api/integrations` | user | What can be installed: code and published integrations in one list of options |
121
- | `GET /api/data-defined/definitions` | user | The published ones, with their counts |
122
- | `POST /api/integrations` with `config: { type: '<id>' }` | user | Install; pins the latest published version and its checksum (`{ type: 'data-defined', definition: { id } }` still works) |
121
+ | `GET /api/integration-definitions` | user | The published ones, with their counts |
122
+ | `POST /api/integrations` with `config: { type: '<id>' }` | user | Install; pins the latest published version and its checksum |
123
123
  | `GET` / `POST /api/integrations/:id/upgrade` `{ to? }` | user | Plan / apply an upgrade (409 with the plan while something is needed) |
124
124
  | `GET /api/integrations/:id/runs` | user | Run history, newest first |
125
125
  | `GET /api/integrations/:id/syncs` | user | Each sync's backfill progress and poll marks |
@@ -153,7 +153,7 @@ code production runs (`renderers/`):
153
153
 
154
154
  | Function | Does |
155
155
  |---|---|
156
- | `previewSurface({ def, installation, surfaceId, record, ports, host? })` | `{ surface, view, html, slack?, hubspot? }`. `slack` is Block Kit (`{ blocks, text, truncated }`), `hubspot` a data description of `@hubspot/ui-extensions` components, `html` the same card framed as its host looks. `host` defaults to the surface's own |
156
+ | `previewSurface({ def, installation, surfaceId, record, ports, host? })` | `{ surface, view, html, slack?, hubspot? }`. `slack` is Block Kit (`{ blocks, text, truncated }`), `hubspot` a data description of `@hubspot/ui-extensions` components (names and props checked against 0.17.1; `props` are real component props, a userAction button carries our own node-level `action` for the card to post, an `open_url` button is a real `href`), `html` the same card framed as its host looks. `host` defaults to the surface's own |
157
157
  | `previewWorkflow({ def, installation, workflowId, event?, record?, manifests, fixtures? })` | Dry run: `{ status, sends, calls, trace, waiting? }`. Each call is `{ stepId, module, capability, kind, args, output, rendered }`; `rendered` is `{ html, slack? }` for writes (a Slack post renders as the message, anything else as a "would call" card) |
158
158
  | `recordingPorts({ def, manifests, fixtures })` | Ports that record and answer instead of calling an API; give them to `previewSurface` for sample data |
159
159
  | `viewToSlack`, `viewToHubSpot`, `viewToHtml`, `slackMessageToHtml` | The renderers on their own. `decodeAction(value)` reads a Slack button click back to `{ integrationId?, workflow, surface }` |
@@ -231,14 +231,14 @@ they were routed under and finish on it.
231
231
  ## Running the tests
232
232
 
233
233
  ```bash
234
- npx jest --config '{}' --rootDir packages/core/integrations/data-defined tests
234
+ npx jest --config '{}' --rootDir packages/core/integrations/definitions tests
235
235
  ```
236
236
 
237
237
  `tests/persistence.test.js` runs the repositories and the publish rules against real databases. Each leg runs only
238
238
  when its URL is set. Point them at a migrated PostgreSQL database and a MongoDB replica set with the schema pushed:
239
239
 
240
240
  ```bash
241
- DATA_DEFINED_POSTGRES_URL=postgresql://… DATA_DEFINED_MONGO_URL=mongodb://… npx jest … tests
241
+ INTEGRATION_DEFINITIONS_POSTGRES_URL=postgresql://… INTEGRATION_DEFINITIONS_MONGO_URL=mongodb://… npx jest … tests
242
242
  ```
243
243
 
244
244
  ## Validating
@@ -251,8 +251,8 @@ Validation is two layers, and a document is valid only when both pass (`schema-c
251
251
 
252
252
  ```bash
253
253
  npm run validate:definitions --workspace @friggframework/core # bundled manifests + examples
254
- node packages/core/integrations/data-defined/scripts/validate.js my.definition.json --strict
255
- node packages/core/integrations/data-defined/scripts/validate.js --manifests ./capabilities --json defs/
254
+ node packages/core/integrations/definitions/scripts/validate.js my.definition.json --strict
255
+ node packages/core/integrations/definitions/scripts/validate.js --manifests ./capabilities --json defs/
256
256
  ```
257
257
 
258
258
  Exit codes: `0` valid, `1` invalid (or a warning with `--strict`), `2` could not run (unreadable JSON, missing file,
@@ -4,16 +4,45 @@ Audit of every `*.capabilities.json` in this directory against the Api class it
4
4
  began as bundled mocks; for each module whose source could be found, every `method` was checked to exist on the real
5
5
  Api class and every call shape was read against the method's real signature.
6
6
 
7
- Re-run it (no network, nothing instantiated):
7
+ Re-run it (no network, nothing instantiated). **One run per module, with that module's checkout first**: the
8
+ api-module-library branches that carry the new methods are separate worktrees, and every worktree also holds an older copy
9
+ of the other modules, so a single run with several roots lets the first root's stale copy of (say) `stripe` win. The
10
+ roots below are the specific module directories, so nothing else can match:
8
11
 
9
12
  ```bash
10
- node packages/core/integrations/data-defined/scripts/check-manifests.js --stub-missing \
11
- ~/phone-app/backend/src/api-modules ~/phone-app/backend/node_modules/@friggframework \
12
- ~/api-module-library/packages/v1-ready ~/api-module-library/packages/needs-updating
13
+ M=packages/core/integrations/definitions/scripts/check-manifests.js
14
+ node $M --stub-missing ~/aml-hubspot/packages/v1-ready/hubspot # hubspot (PR #106)
15
+ node $M --stub-missing ~/aml-salesforce/packages/v1-ready/salesforce # salesforce (PR #107)
16
+ node $M --stub-missing ~/aml-stripe/packages/v1-ready/stripe # stripe (PR #108)
17
+ node $M --stub-missing ~/aml-linear/packages/v1-ready/linear # linear (PR #109)
18
+ node $M --stub-missing ~/aml-small/packages/v1-ready # asana, fathom (PR #110)
19
+ node $M --stub-missing ~/aml-small/packages/needs-updating # slack (PR #110)
20
+ node $M --stub-missing <built pipedrive> # pipedrive (PR #110): src/api.ts has no dist, so
21
+ # compile it first (tsc --outDir dist --noCheck)
22
+ node $M --stub-missing ~/phone-app/backend/src/api-modules # phone (the app's own module)
13
23
  ```
14
24
 
15
- (`--stub-missing` stands in for dependencies that are not installed here, such as jsforce, stripe and @linear/sdk, so
16
- the Api's methods can be read. Roots are searched in order and the first module that loads wins.)
25
+ Each run reports every manifest, so read only the line for the module you are checking (the others resolve to nothing or
26
+ to an older copy). Run from the repo root: loading an Api pulls in `@friggframework/core`, which reads `package.json`
27
+ from the working directory. (`--stub-missing` stands in for dependencies that are not installed here, such as jsforce,
28
+ stripe and @linear/sdk, so the Api's methods can be read.)
29
+
30
+ ## Methods that landed after this audit
31
+
32
+ The capabilities this file first marked unimplemented for hubspot, salesforce, stripe, linear, asana, pipedrive, fathom
33
+ and slack now have methods, on unmerged api-module-library branches: **#106 (hubspot), #107 (salesforce), #108 (stripe),
34
+ #109 (linear), #110 (asana, pipedrive, fathom, slack)**, stacked on #105. The manifests describe those methods, so **a
35
+ definition that uses them needs the module releases that carry those PRs**; against an older release the call fails at
36
+ runtime with "not a function". Exceptions that stay unimplemented: Salesforce's signed webhook (it has none: CDC arrives
37
+ over the Pub/Sub stream and Outbound Messages are unsigned), and Phone's `contact.search`, which is not part of that work.
38
+ Two shape notes:
39
+
40
+ - HubSpot `deal.search` was reshaped to the real `searchDeals({ filterGroups, sorts, props, limit, after, allProps })`;
41
+ the old open/closeDateBefore/closeDateAfter/minAmount inputs never existed in the Api and are now `filterGroups`
42
+ filters (`hs_is_closed`, `closedate`, `amount`). The Api returns HubSpot's `{ results, paging }` page, not a bare array,
43
+ so a step that indexes the result as a list (`$.steps.deals[0]`) reads the page object: the `hubspot-slack` example still
44
+ does and is a mock-era shape to revisit.
45
+ - HubSpot `deal.update` is `updateDeal({ dealId, properties })`: one object, so the input is now `dealId` (it was `id`).
17
46
 
18
47
  ## How to read this
19
48
 
@@ -29,31 +58,32 @@ the Api's methods can be read. Roots are searched in order and the first module
29
58
  `runtime.js` `argumentsFor` spreads them. An input that is declared but never passed is refused by the manifest check,
30
59
  because it would be accepted and silently dropped.
31
60
  - **Webhook verification.** Every manifest names `verifyWebhookSignature`. No real module has it (only clio, which has no
32
- manifest). Each real module's `webhook` block is marked `"status": "unimplemented"`, and a workflow that triggers on
61
+ manifest). Each real module's `webhook` block was marked `"status": "unimplemented"`; since api-module-library
62
+ 659b90d eight of them verify signatures (Salesforce has no signed webhook and stays unimplemented), and a workflow that triggers on
33
63
  its events validates with a warning: every delivery would fail signature verification.
34
64
 
35
65
  ## Summary
36
66
 
37
67
  | Module | Source checked | Capabilities | Renamed method | Call shape fixed | Inputs changed | Unimplemented upstream |
38
68
  |---|---|---|---|---|---|---|
39
- | asana | ~/api-module-library/packages/v1-ready/asana | 6 | 3 | 1 | 2 | 3 |
69
+ | asana | ~/api-module-library/packages/v1-ready/asana | 6 | 3 | 1 | 2 | 0 |
40
70
  | calendly | none found | 6 | - | - | - | - |
41
71
  | docusign | none found | 5 | - | - | - | - |
42
- | fathom | ~/api-module-library/packages/v1-ready/fathom | 5 | 2 | 2 | 0 | 1 |
72
+ | fathom | ~/api-module-library/packages/v1-ready/fathom | 5 | 2 | 2 | 0 | 0 |
43
73
  | github | none found | 6 | - | - | - | - |
44
- | hubspot | ~/api-module-library/packages/v1-ready/hubspot | 23 | 0 | 5 | 1 | 17 |
74
+ | hubspot | ~/api-module-library/packages/v1-ready/hubspot | 23 | 0 | 5 | 1 | 0 |
45
75
  | jira | none found | 12 | - | - | - | - |
46
76
  | klaviyo | none found | 14 | - | - | - | - |
47
- | linear | ~/api-module-library/packages/v1-ready/linear | 9 | 0 | 0 | 0 | 9 |
77
+ | linear | ~/api-module-library/packages/v1-ready/linear | 9 | 0 | 0 | 0 | 0 |
48
78
  | netsuite | none found | 15 | - | - | - | - |
49
79
  | notion | none found | 6 | - | - | - | - |
50
- | pipedrive | ~/phone-app/backend/node_modules/@friggframework/api-module-pipedrive | 11 | 4 | 3 | 1 | 2 |
80
+ | pipedrive | ~/aml-small/packages/v1-ready/pipedrive (compiled src/api.ts) | 11 | 4 | 3 | 1 | 0 |
51
81
  | quickbooks | none found | 14 | - | - | - | - |
52
82
  | phone | ~/phone-app/backend/src/api-modules/phone | 13 | 1 | 4 | 0 | 1 |
53
- | salesforce | ~/api-module-library/packages/v1-ready/salesforce | 12 | 5 | 5 | 0 | 7 |
83
+ | salesforce | ~/api-module-library/packages/v1-ready/salesforce | 12 | 5 | 5 | 0 | 0 (webhook signing aside) |
54
84
  | shopify | none found | 15 | - | - | - | - |
55
- | slack | ~/api-module-library/packages/needs-updating/slack | 7 | 2 | 1 | 0 | 1 |
56
- | stripe | ~/api-module-library/packages/v1-ready/stripe | 9 | 1 | 0 | 0 | 8 |
85
+ | slack | ~/api-module-library/packages/needs-updating/slack | 7 | 2 | 1 | 0 | 0 |
86
+ | stripe | ~/api-module-library/packages/v1-ready/stripe | 9 | 1 | 0 | 0 | 0 |
57
87
  | acmeerp | none found | 13 | - | - | - | - |
58
88
  | webflow | none found | 5 | - | - | - | - |
59
89
  | zendesk | none found | 8 | - | - | - | - |
@@ -63,8 +93,7 @@ nothing in them is known to exist.
63
93
 
64
94
  Where a module has two candidates the first root given wins. Phone is checked against the deployed
65
95
  `phone-app/backend/src/api-modules/phone` (the app's own module: `createUnifiedWebhook`, `updateContact`). The library's `v1-ready/phone` 1.0.0 lacks `updateContact` and has `createCallWebhook` and friends
66
- instead of one webhook method. Pipedrive is checked against the built `@friggframework/api-module-pipedrive`
67
- 2.1.0-next.7 (the library copy is TypeScript source with no `dist`).
96
+ instead of one webhook method. Pipedrive is checked against the compiled TypeScript source of the api-module-library branch (the library copy has no `dist`).
68
97
 
69
98
  Manifest versions in the files (for example HubSpot 1.4.0, Asana 1.5.1) are ahead of the module sources audited (HubSpot
70
99
  1.1.7, Asana 1.1.5): the manifests describe modules that were never released as written.
@@ -76,12 +105,12 @@ Checked against `~/api-module-library/packages/v1-ready/asana` (@friggframework/
76
105
  | Capability | Method | Status | Real signature | Note |
77
106
  |---|---|---|---|---|
78
107
  | `task.get` | `getTaskById` | ok | `getTaskById(id)` | Api.getTaskById(id) takes only the id; field selection (opt_fields) is not supported. (method was `getTask`; called with one object; now positional; inputs were taskGid, opt_fields) |
79
- | `task.search` | `searchTasksInWorkspace` | unimplemented | none | Api.listTasks({ workspaceId, assigneeId }) filters by workspace and assignee only; due_on, completed, projects_any and limit have no equivalent. |
108
+ | `task.search` | `searchTasksInWorkspace` | ok (unreleased) | `searchTasksInWorkspace(workspaceId, params = {})` | Implemented in api-module-library #110; needs the release that includes it. |
80
109
  | `project.list` | `listProjects` | ok | `listProjects(params)` | (method was `getProjects`; inputs were workspaceGid, archived) |
81
110
  | `workspace.list` | `listWorkspaces` | ok | `listWorkspaces()` | (method was `getWorkspaces`) |
82
- | `webhook.create` | `createWebhook` | unimplemented | none | Api has no webhook methods. |
83
- | `webhook.delete` | `deleteWebhook` | unimplemented | none | Api has no webhook methods. |
84
- | `webhook.verify` | `verifyWebhookSignature` | unimplemented | | The asana Api has no verifyWebhookSignature method; webhook signatures cannot be checked until the module adds one. |
111
+ | `webhook.create` | `createWebhook` | ok (unreleased) | `createWebhook(resource, target, filters)` | Implemented in api-module-library #110; needs the release that includes it. |
112
+ | `webhook.delete` | `deleteWebhook` | ok (unreleased) | `deleteWebhook(webhookGid)` | Implemented in api-module-library #110; needs the release that includes it. |
113
+ | `webhook.verify` | `verifyWebhookSignature` | ok (unreleased) | `verifyWebhookSignature({ headers, body, rawBody, method?, url?, now? })` | Implemented in api-module-library 659b90d per the vendor's signing docs; needs the release that includes it. |
85
114
 
86
115
  ## Calendly (`calendly`)
87
116
 
@@ -120,8 +149,8 @@ Checked against `~/api-module-library/packages/v1-ready/fathom` (@friggframework
120
149
  | `meeting.summary` | `getSummary` | ok | `getSummary(recordingId, params = {})` | (method was `getRecordingSummary`; called with one object; now positional) |
121
150
  | `meeting.transcript` | `getTranscript` | ok | `getTranscript(recordingId, params = {})` | (method was `getRecordingTranscript`; called with one object; now positional) |
122
151
  | `webhook.create` | `createWebhook` | ok | `createWebhook(data)` | |
123
- | `webhook.delete` | `deleteWebhook` | unimplemented | none | Api has createWebhook but no deleteWebhook. |
124
- | `webhook.verify` | `verifyWebhookSignature` | unimplemented | | The fathom Api has no verifyWebhookSignature method; webhook signatures cannot be checked until the module adds one. |
152
+ | `webhook.delete` | `deleteWebhook` | ok (unreleased) | `deleteWebhook(id)` | Implemented in api-module-library #110; needs the release that includes it. |
153
+ | `webhook.verify` | `verifyWebhookSignature` | ok (unreleased) | `verifyWebhookSignature({ headers, body, rawBody, method?, url?, now? })` | Implemented in api-module-library 659b90d per the vendor's signing docs; needs the release that includes it. |
125
154
 
126
155
  ## GitHub (`github`)
127
156
 
@@ -144,29 +173,29 @@ Checked against `~/api-module-library/packages/v1-ready/hubspot` (@friggframewor
144
173
  | Capability | Method | Status | Real signature | Note |
145
174
  |---|---|---|---|---|
146
175
  | `contact.get` | `getContactById` | ok | `getContactById(contactId)` | Api.getContactById(id) always requests the portal's full property list; a properties argument is not supported. (called with one object; now positional; inputs were id, properties) |
147
- | `contact.search` | `searchContacts` | unimplemented | none | Api has no contact search (searchCompanies, searchDeals, searchCustomObjects and searchLists only). |
176
+ | `contact.search` | `searchContacts` | ok (unreleased) | `searchContacts(params = {})` | Implemented in api-module-library #106; needs the release that includes it. |
148
177
  | `contact.list` | `listContacts` | ok | `listContacts(params)` | Api.listContacts({ after, limit, properties }) has no modified-since filter, so updatedSince is ignored and a poll re-lists every contact. |
149
178
  | `contact.create` | `createContact` | ok | `createContact(body)` | (called with one object; now positional) |
150
179
  | `contact.update` | `updateContact` | ok | `updateContact(contactId, properties)` | (called with one object; now positional) |
151
180
  | `deal.get` | `getDealById` | ok | `getDealById(dealId)` | (called with one object; now positional) |
152
- | `deal.listForContact` | `listDealsForContact` | unimplemented | none | Api has no per-contact deal listing; associations come from getBatchAssociations(from, to, inputs). |
153
- | `deal.contacts` | `listContactsForDeal` | unimplemented | none | Api has no per-deal contact listing; associations come from getBatchAssociations(from, to, inputs). |
154
- | `task.create` | `createTask` | unimplemented | none | Api has no engagement or task methods. |
155
- | `call.create` | `createCallEngagement` | unimplemented | none | Api has no engagement or task methods. |
156
- | `note.create` | `createNote` | unimplemented | none | Api has no engagement or task methods. |
157
- | `owner.list` | `listOwners` | unimplemented | none | Api has getOwnerById only. |
158
- | `pipeline.stages` | `listDealStages` | unimplemented | none | Api.getPipelineDetails(objType) returns every pipeline for an object type, not the stages of one pipeline. |
159
- | `contact.upsertByEmail` | `upsertContactByEmail` | unimplemented | none | Api has no upsert and no contact search to build one from. |
160
- | `deal.update` | `updateDeal` | unimplemented (a method of that name exists, unusable as the capability is shaped) | `updateDeal(params)` | Api.updateDeal({ dealId, properties }) reads this.URLs.getDealById, which is not defined (the table has dealById), so it throws before sending. Fixed in api-module-library (claude/shared-conversation-8hgmbr, 5961c9a); stays unimplemented here until a release carries it. |
161
- | `deal.search` | `searchDeals` | unimplemented (a method of that name exists, unusable as the capability is shaped) | `searchDeals(params)` | Api.searchDeals takes HubSpot filterGroups, sorts, props, limit and after, not open/closeDate/amount filters. |
162
- | `task.get` | `getTaskById` | unimplemented | none | Api has no engagement or task methods. |
163
- | `task.update` | `updateTask` | unimplemented | none | Api has no engagement or task methods. |
164
- | `task.search` | `searchTasks` | unimplemented | none | Api has no engagement or task methods. |
181
+ | `deal.listForContact` | `listDealsForContact` | ok (unreleased) | `listDealsForContact(contactId, open)` | Implemented in api-module-library #106; needs the release that includes it. |
182
+ | `deal.contacts` | `listContactsForDeal` | ok (unreleased) | `listContactsForDeal(dealId)` | Implemented in api-module-library #106; needs the release that includes it. |
183
+ | `task.create` | `createTask` | ok (unreleased) | `createTask(properties, contactId)` | Implemented in api-module-library #106; needs the release that includes it. |
184
+ | `call.create` | `createCallEngagement` | ok (unreleased) | `createCallEngagement(properties, contactId)` | Implemented in api-module-library #106; needs the release that includes it. |
185
+ | `note.create` | `createNote` | ok (unreleased) | `createNote(properties, contactId, dealId)` | Implemented in api-module-library #106; needs the release that includes it. |
186
+ | `owner.list` | `listOwners` | ok (unreleased) | `listOwners()` | Implemented in api-module-library #106; needs the release that includes it. |
187
+ | `pipeline.stages` | `listDealStages` | ok (unreleased) | `listDealStages(pipelineId)` | Implemented in api-module-library #106; needs the release that includes it. |
188
+ | `contact.upsertByEmail` | `upsertContactByEmail` | ok (unreleased) | `upsertContactByEmail(email, properties)` | Implemented in api-module-library #106; needs the release that includes it. |
189
+ | `deal.update` | `updateDeal` | ok (unreleased) | `updateDeal(params)` | Api.updateDeal({ dealId, properties }) takes one object, so the input is dealId (it was id). Implemented in api-module-library #106; needs the release that includes it. |
190
+ | `deal.search` | `searchDeals` | ok (unreleased) | `searchDeals(params)` | Api.searchDeals takes HubSpot filterGroups ([{ filters: [{ propertyName, operator, value }] }]), sorts, props, limit, after and allProps (default true: every deal property when props is empty). The capability was reshaped from open/closeDateBefore/closeDateAfter/minAmount, which the Api never had; a definition expresses those as filterGroups. Implemented in api-module-library #106; needs the release that includes it. |
191
+ | `task.get` | `getTaskById` | ok (unreleased) | `getTaskById(id, properties)` | Implemented in api-module-library #106; needs the release that includes it. |
192
+ | `task.update` | `updateTask` | ok (unreleased) | `updateTask(id, properties)` | Implemented in api-module-library #106; needs the release that includes it. |
193
+ | `task.search` | `searchTasks` | ok (unreleased) | `searchTasks(params = {})` | Implemented in api-module-library #106; needs the release that includes it. |
165
194
  | `owner.get` | `getOwnerById` | ok | `getOwnerById(ownerId)` | (called with one object; now positional) |
166
- | `owner.findByEmail` | `getOwnerByEmail` | unimplemented | none | Api has getOwnerById only. |
167
- | `subscription.definitions` | `listSubscriptionDefinitions` | unimplemented | none | Api has no email subscription methods. |
168
- | `subscription.unsubscribe` | `unsubscribeEmail` | unimplemented | none | Api has no email subscription methods. |
169
- | `webhook.verify` | `verifyWebhookSignature` | unimplemented | | The hubspot Api has no verifyWebhookSignature method; webhook signatures cannot be checked until the module adds one. |
195
+ | `owner.findByEmail` | `getOwnerByEmail` | ok (unreleased) | `getOwnerByEmail(email)` | Implemented in api-module-library #106; needs the release that includes it. |
196
+ | `subscription.definitions` | `listSubscriptionDefinitions` | ok (unreleased) | `listSubscriptionDefinitions()` | Implemented in api-module-library #106; needs the release that includes it. |
197
+ | `subscription.unsubscribe` | `unsubscribeEmail` | ok (unreleased) | `unsubscribeEmail(emailAddress, subscriptionId, legalBasis, legalBasisExplanation)` | Implemented in api-module-library #106; needs the release that includes it. |
198
+ | `webhook.verify` | `verifyWebhookSignature` | ok (unreleased) | `verifyWebhookSignature({ headers, body, rawBody, method?, url?, now? })` | Implemented in api-module-library 659b90d per the vendor's signing docs; needs the release that includes it. |
170
199
 
171
200
  ## Jira Cloud (`jira`)
172
201
 
@@ -216,16 +245,16 @@ Checked against `~/api-module-library/packages/v1-ready/linear` (@friggframework
216
245
 
217
246
  | Capability | Method | Status | Real signature | Note |
218
247
  |---|---|---|---|---|
219
- | `issue.get` | `issue` | unimplemented | none | Api exposes getClient() (a @linear/sdk LinearClient) plus getUsers, getProjects and getUserIssues; issues, comments, attachments, teams and workflow states are reached through the SDK client, not Api methods. |
220
- | `issue.byIdentifier` | `issueByIdentifier` | unimplemented | none | Api exposes getClient() (a @linear/sdk LinearClient) plus getUsers, getProjects and getUserIssues; issues, comments, attachments, teams and workflow states are reached through the SDK client, not Api methods. |
221
- | `issue.search` | `searchIssues` | unimplemented | none | Api exposes getClient() (a @linear/sdk LinearClient) plus getUsers, getProjects and getUserIssues; issues, comments, attachments, teams and workflow states are reached through the SDK client, not Api methods. |
222
- | `issue.create` | `issueCreate` | unimplemented | none | Api exposes getClient() (a @linear/sdk LinearClient) plus getUsers, getProjects and getUserIssues; issues, comments, attachments, teams and workflow states are reached through the SDK client, not Api methods. |
223
- | `issue.update` | `issueUpdate` | unimplemented | none | Api exposes getClient() (a @linear/sdk LinearClient) plus getUsers, getProjects and getUserIssues; issues, comments, attachments, teams and workflow states are reached through the SDK client, not Api methods. |
224
- | `comment.create` | `commentCreate` | unimplemented | none | Api exposes getClient() (a @linear/sdk LinearClient) plus getUsers, getProjects and getUserIssues; issues, comments, attachments, teams and workflow states are reached through the SDK client, not Api methods. |
225
- | `attachment.link` | `attachmentLinkURL` | unimplemented | none | Api exposes getClient() (a @linear/sdk LinearClient) plus getUsers, getProjects and getUserIssues; issues, comments, attachments, teams and workflow states are reached through the SDK client, not Api methods. |
226
- | `team.list` | `teams` | unimplemented | none | Api exposes getClient() (a @linear/sdk LinearClient) plus getUsers, getProjects and getUserIssues; issues, comments, attachments, teams and workflow states are reached through the SDK client, not Api methods. |
227
- | `workflowState.list` | `workflowStates` | unimplemented | none | Api exposes getClient() (a @linear/sdk LinearClient) plus getUsers, getProjects and getUserIssues; issues, comments, attachments, teams and workflow states are reached through the SDK client, not Api methods. |
228
- | `webhook.verify` | `verifyWebhookSignature` | unimplemented | | The linear Api has no verifyWebhookSignature method; webhook signatures cannot be checked until the module adds one. |
248
+ | `issue.get` | `issue` | ok (unreleased) | `issue(id)` | Implemented in api-module-library #109; needs the release that includes it. |
249
+ | `issue.byIdentifier` | `issueByIdentifier` | ok (unreleased) | `issueByIdentifier(identifier)` | Implemented in api-module-library #109; needs the release that includes it. |
250
+ | `issue.search` | `searchIssues` | ok (unreleased) | `searchIssues(teamId, stateType, updatedBefore, first)` | Implemented in api-module-library #109; needs the release that includes it. |
251
+ | `issue.create` | `issueCreate` | ok (unreleased) | `issueCreate(teamId, title, description, priority, stateName, labelIds)` | Implemented in api-module-library #109; needs the release that includes it. |
252
+ | `issue.update` | `issueUpdate` | ok (unreleased) | `issueUpdate(id, input)` | Implemented in api-module-library #109; needs the release that includes it. |
253
+ | `comment.create` | `commentCreate` | ok (unreleased) | `commentCreate(issueId, body)` | Implemented in api-module-library #109; needs the release that includes it. |
254
+ | `attachment.link` | `attachmentLinkURL` | ok (unreleased) | `attachmentLinkURL(issueId, url, title)` | Implemented in api-module-library #109; needs the release that includes it. |
255
+ | `team.list` | `teams` | ok (unreleased) | `teams()` | Implemented in api-module-library #109; needs the release that includes it. |
256
+ | `workflowState.list` | `workflowStates` | ok (unreleased) | `workflowStates(teamId, name)` | Implemented in api-module-library #109; needs the release that includes it. |
257
+ | `webhook.verify` | `verifyWebhookSignature` | ok (unreleased) | `verifyWebhookSignature({ headers, body, rawBody, method?, url?, now? })` | Implemented in api-module-library 659b90d per the vendor's signing docs; needs the release that includes it. |
229
258
 
230
259
  ## NetSuite (`netsuite`)
231
260
 
@@ -265,22 +294,22 @@ Checked against `~/api-module-library/packages/v1-ready/linear` (@friggframework
265
294
 
266
295
  ## Pipedrive (`pipedrive`)
267
296
 
268
- Checked against `~/phone-app/backend/node_modules/@friggframework/api-module-pipedrive` (@friggframework/api-module-pipedrive manifest 2.1.0).
297
+ Checked against the api-module-library branch source `~/aml-small/packages/v1-ready/pipedrive` (src/api.ts, compiled; PR #110), manifest 2.1.0. The built 2.1.0 in `~/phone-app/backend/node_modules/@friggframework` predates `addPerson`/`updatePerson`.
269
298
 
270
299
  | Capability | Method | Status | Real signature | Note |
271
300
  |---|---|---|---|---|
272
301
  | `person.get` | `getPerson` | ok | `getPerson(personId, params)` | (called with one object; now positional) |
273
302
  | `person.search` | `searchPersons` | ok | `searchPersons(params)` | |
274
303
  | `person.list` | `listPersons` | ok | `listPersons(params)` | |
275
- | `person.create` | `addPerson` | unimplemented | none | Api has no addPerson (persons can be listed, fetched and searched only). |
276
- | `person.update` | `updatePerson` | unimplemented | none | Api has no updatePerson. |
304
+ | `person.create` | `addPerson` | ok (unreleased) | `addPerson(person)` | Implemented in api-module-library #110; needs the release that includes it. |
305
+ | `person.update` | `updatePerson` | ok (unreleased) | `updatePerson(personId, person)` | Implemented in api-module-library #110; needs the release that includes it. |
277
306
  | `activity.create` | `createActivity` | ok | `createActivity(params)` | (method was `addActivity`; called with one object; now positional) |
278
307
  | `activity.list` | `listActivities` | ok | `listActivities(params)` | |
279
308
  | `deal.listForPerson` | `listDeals` | ok | `listDeals(params)` | (method was `listPersonDeals`; inputs were personId, status) |
280
309
  | `user.list` | `listUsers` | ok | `listUsers()` | (method was `getUsers`) |
281
310
  | `webhook.create` | `createWebhook` | ok | `createWebhook(params)` | (method was `addWebhook`) |
282
311
  | `webhook.delete` | `deleteWebhook` | ok | `deleteWebhook(webhookId)` | (called with one object; now positional) |
283
- | `webhook.verify` | `verifyWebhookSignature` | unimplemented | | The pipedrive Api has no verifyWebhookSignature method; webhook signatures cannot be checked until the module adds one. |
312
+ | `webhook.verify` | `verifyWebhookSignature` | ok (unreleased) | `verifyWebhookSignature({ headers, body, rawBody, method?, url?, now? })` | Implemented in api-module-library 659b90d per the vendor's signing docs; needs the release that includes it. |
284
313
 
285
314
  ## QuickBooks Online (`quickbooks`)
286
315
 
@@ -323,7 +352,7 @@ Checked against `~/phone-app/backend/src/api-modules/phone` (@acme/api-module-ph
323
352
  | `user.list` | `listUsers` | ok | `listUsers(params = {})` | |
324
353
  | `webhook.create` | `createUnifiedWebhook` | ok | `createUnifiedWebhook(data)` | (method was `createWebhook`) |
325
354
  | `webhook.delete` | `deleteWebhook` | ok | `deleteWebhook(id)` | (called with one object; now positional) |
326
- | `webhook.verify` | `verifyWebhookSignature` | unimplemented | | The phone Api has no verifyWebhookSignature method; webhook signatures cannot be checked until the module adds one. |
355
+ | `webhook.verify` | `verifyWebhookSignature` | ok (unreleased) | `verifyWebhookSignature({ headers, body, rawBody, method?, url?, now? })` | Implemented in api-module-library 659b90d per the vendor's signing docs; needs the release that includes it. |
327
356
 
328
357
  ## Salesforce (`salesforce`)
329
358
 
@@ -333,17 +362,17 @@ Checked against `~/api-module-library/packages/v1-ready/salesforce` (@friggframe
333
362
  |---|---|---|---|---|
334
363
  | `lead.get` | `get` | ok | `get(object, id)` | (method was `getLead`; called with one object; now positional) |
335
364
  | `lead.create` | `create` | ok | `create(object, data)` | (method was `createLead`; called with one object; now positional) |
336
- | `lead.update` | `updateLead` | unimplemented | none | Api.update(object, data) needs the record id inside data (data.Id); the capability passes the id separately, so there is no way to call it. |
337
- | `person.searchByEmail` | `searchPeopleByEmail` | unimplemented | none | Api.find(object, filter) searches one sObject at a time; searching Lead and Contact together would need two calls. |
365
+ | `lead.update` | `updateLead` | ok (unreleased) | `updateLead(id, fields)` | Implemented in api-module-library #107; needs the release that includes it. |
366
+ | `person.searchByEmail` | `searchPeopleByEmail` | ok (unreleased) | `searchPeopleByEmail(email)` | Implemented in api-module-library #107; needs the release that includes it. |
338
367
  | `event.create` | `create` | ok | `create(object, data)` | (method was `createEvent`; called with one object; now positional) |
339
- | `event.update` | `updateEvent` | unimplemented | none | Api.update(object, data) needs the record id inside data (data.Id); the capability passes the id separately, so there is no way to call it. |
368
+ | `event.update` | `updateEvent` | ok (unreleased) | `updateEvent(id, fields)` | Implemented in api-module-library #107; needs the release that includes it. |
340
369
  | `opportunity.get` | `get` | ok | `get(object, id)` | (method was `getOpportunity`; called with one object; now positional) |
341
- | `opportunity.update` | `updateOpportunity` | unimplemented | none | Api.update(object, data) needs the record id inside data (data.Id); the capability passes the id separately, so there is no way to call it. |
370
+ | `opportunity.update` | `updateOpportunity` | ok (unreleased) | `updateOpportunity(id, fields)` | Implemented in api-module-library #107; needs the release that includes it. |
342
371
  | `task.create` | `create` | ok | `create(object, data)` | (method was `createTask`; called with one object; now positional) |
343
- | `file.attach` | `createContentVersion` | unimplemented | none | Api has the generic create(object, data) only; ContentVersion field names differ from this capability's inputs. |
344
- | `note.create` | `createContentNote` | unimplemented | none | Api has the generic create(object, data) only; ContentNote field names differ from this capability's inputs. |
345
- | `user.list` | `listUsers` | unimplemented | none | Api has find(object, filter) but no user listing with an activeOnly filter. |
346
- | `webhook.verify` | `verifyWebhookSignature` | unimplemented | | The salesforce Api has no verifyWebhookSignature method; webhook signatures cannot be checked until the module adds one. |
372
+ | `file.attach` | `createContentVersion` | ok (unreleased) | `createContentVersion(title, pathOnClient, versionData, firstPublishLocationId)` | Implemented in api-module-library #107; needs the release that includes it. |
373
+ | `note.create` | `createContentNote` | ok (unreleased) | `createContentNote(title, content, linkedEntityId)` | Implemented in api-module-library #107; needs the release that includes it. |
374
+ | `user.list` | `listUsers` | ok (unreleased) | `listUsers(activeOnly = false)` | Implemented in api-module-library #107; needs the release that includes it. |
375
+ | `webhook.verify` | `verifyWebhookSignature` | unimplemented | | Not a webhook: CDC arrives over the Pub/Sub or CometD stream; Outbound Messages are unsigned (OrganizationId + IP allowlist). Deliveries are rejected. |
347
376
 
348
377
  ## Shopify (`shopify`)
349
378
 
@@ -379,9 +408,9 @@ Checked against `~/api-module-library/packages/needs-updating/slack` (@friggfram
379
408
  | `message.postEphemeral` | `postEphemeral` | ok | `postEphemeral(body)` | |
380
409
  | `message.permalink` | `getMessagePermalink` | ok | `getMessagePermalink(query)` | (method was `getPermalink`) |
381
410
  | `channel.list` | `listChannels` | ok | `listChannels(query)` | (method was `listConversations`) |
382
- | `user.list` | `listUsers` | unimplemented | none | Api has lookupUserByEmail, getUserById and getUserProfileById, but no user listing. |
411
+ | `user.list` | `listUsers` | ok (unreleased) | `listUsers(query = {})` | Implemented in api-module-library #110; needs the release that includes it. |
383
412
  | `user.lookupByEmail` | `lookupUserByEmail` | ok | `lookupUserByEmail(email)` | (called with one object; now positional) |
384
- | `webhook.verify` | `verifyWebhookSignature` | unimplemented | | The slack Api has no verifyWebhookSignature method; webhook signatures cannot be checked until the module adds one. |
413
+ | `webhook.verify` | `verifyWebhookSignature` | ok (unreleased) | `verifyWebhookSignature({ headers, body, rawBody, method?, url?, now? })` | Implemented in api-module-library 659b90d per the vendor's signing docs; needs the release that includes it. |
385
414
 
386
415
  ## Stripe (`stripe`)
387
416
 
@@ -389,16 +418,16 @@ Checked against `~/api-module-library/packages/v1-ready/stripe` (@friggframework
389
418
 
390
419
  | Capability | Method | Status | Real signature | Note |
391
420
  |---|---|---|---|---|
392
- | `customer.get` | `retrieveCustomer` | unimplemented | none | Api wraps balance transactions, charges, accounts and webhook endpoints only; customers, payment intents, refunds and payouts are not exposed. |
393
- | `customer.list` | `listCustomers` | unimplemented | none | Api wraps balance transactions, charges, accounts and webhook endpoints only; customers, payment intents, refunds and payouts are not exposed. |
394
- | `customer.search` | `searchCustomers` | unimplemented | none | Api wraps balance transactions, charges, accounts and webhook endpoints only; customers, payment intents, refunds and payouts are not exposed. |
395
- | `customer.create` | `createCustomer` | unimplemented | none | Api wraps balance transactions, charges, accounts and webhook endpoints only; customers, payment intents, refunds and payouts are not exposed. |
396
- | `customer.update` | `updateCustomer` | unimplemented | none | Api wraps balance transactions, charges, accounts and webhook endpoints only; customers, payment intents, refunds and payouts are not exposed. |
397
- | `paymentIntent.get` | `retrievePaymentIntent` | unimplemented | none | Api wraps balance transactions, charges, accounts and webhook endpoints only; customers, payment intents, refunds and payouts are not exposed. |
398
- | `refund.get` | `retrieveRefund` | unimplemented | none | Api wraps balance transactions, charges, accounts and webhook endpoints only; customers, payment intents, refunds and payouts are not exposed. |
399
- | `payout.list` | `listPayouts` | unimplemented | none | Api wraps balance transactions, charges, accounts and webhook endpoints only; customers, payment intents, refunds and payouts are not exposed. |
421
+ | `customer.get` | `retrieveCustomer` | ok (unreleased) | `retrieveCustomer(id, params = {}, options = {})` | Implemented in api-module-library #108; needs the release that includes it. |
422
+ | `customer.list` | `listCustomers` | ok (unreleased) | `listCustomers(params = {}, options = {})` | Implemented in api-module-library #108; needs the release that includes it. |
423
+ | `customer.search` | `searchCustomers` | ok (unreleased) | `searchCustomers(query, params = {}, options = {})` | Implemented in api-module-library #108; needs the release that includes it. |
424
+ | `customer.create` | `createCustomer` | ok (unreleased) | `createCustomer(params = {}, options = {})` | Implemented in api-module-library #108; needs the release that includes it. |
425
+ | `customer.update` | `updateCustomer` | ok (unreleased) | `updateCustomer(id, params = {}, options = {})` | Implemented in api-module-library #108; needs the release that includes it. |
426
+ | `paymentIntent.get` | `retrievePaymentIntent` | ok (unreleased) | `retrievePaymentIntent(id, params = {}, options = {})` | Implemented in api-module-library #108; needs the release that includes it. |
427
+ | `refund.get` | `retrieveRefund` | ok (unreleased) | `retrieveRefund(id, params = {}, options = {})` | Implemented in api-module-library #108; needs the release that includes it. |
428
+ | `payout.list` | `listPayouts` | ok (unreleased) | `listPayouts(params = {}, options = {})` | Implemented in api-module-library #108; needs the release that includes it. |
400
429
  | `balanceTransaction.list` | `getBalanceTransactions` | ok | `getBalanceTransactions(params)` | (method was `listBalanceTransactions`) |
401
- | `webhook.verify` | `verifyWebhookSignature` | unimplemented | | The stripe Api has no verifyWebhookSignature method; webhook signatures cannot be checked until the module adds one. |
430
+ | `webhook.verify` | `verifyWebhookSignature` | ok (unreleased) | `verifyWebhookSignature({ headers, body, rawBody, method?, url?, now? })` | Implemented in api-module-library 659b90d per the vendor's signing docs; needs the release that includes it. |
402
431
 
403
432
  ## Acme ERP (`acmeerp`)
404
433