AgentStack
Browse Sign in
Browse Why AgentStack Sell Docs
Sign in
SKILL verified MIT Self-run

Writing Scripts

skill-celigo-ai-writing-scripts · by celigo

Write Celigo JavaScript hook scripts -- preSavePage, preMap, postMap, postSubmit, postResponseMap, filter, transform, branching, handleRequest. Use when creating or editing scripts, choosing the right hook point, understanding input/output data shapes, or debugging script behavior.

No reviews yet
0 installs
7 views
0.0% view→install

Install

$ agentstack add skill-celigo-ai-writing-scripts

✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access No
  • Filesystem access No
  • Shell / process execution No
  • Environment & secrets No
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

View the full security report →

Verified badge

Passed review? Show it. Paste this badge into your README, it links to the public security report.

AgentStack Verified badge Links to your public security report.
[![AgentStack Verified](https://agentstack.voostack.com/badges/verified.svg)](https://agentstack.voostack.com/security/report/skill-celigo-ai-writing-scripts)

Reliability & compatibility

Security review passed
0 installs to date
no reviews yet
1mo ago

Declared compatibility

Claude CodeClaude Desktop

Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.

Preview Execution monitoring

We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.

How agent discovery & health will work →
Are you the author of Writing Scripts? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

Writing Scripts

A script is a JavaScript function that runs at a specific hook point in the Celigo data pipeline. Scripts handle logic that expressions, filters, and visual mappings cannot -- complex conditionals, cross-record calculations, API calls within the pipeline, and custom routing.

Concerns when writing a script:

  • Choosing the right hook point -- which function type matches what you're trying to accomplish
  • Input/output contracts -- what options contains and what the function must return (array length rules are strict)
  • Expression alternative -- filter, transform, and output filter have expression-based alternatives that don't require a script; prefer expressions when possible
  • Available modules -- scripts can import three built-in modules: integrator-api (call Celigo APIs), dayjs (date/time manipulation), and sjcl (Stanford JavaScript Crypto Library for hashing/encryption)
  • One script, many functions -- a single script resource can contain multiple exported functions, each wired independently to different hook points

Used across flows, APIs, and tools.

Hook Points

Every script function runs at a specific point in the pipeline. Choose based on when you need to act and what data you need access to.

Data Pipeline Hooks

| Hook | Runs on | When | Input | Must return | |------|---------|------|-------|-------------| | preSavePage | Export | After retrieval, before pipeline | options.data[], errors[], files[], retryData{} | { data[], errors[], abort, newErrorsAndRetryData[] } | | preMap | Import | Before field mapping | options.data[] (unmapped records) | Array matching data.length: { data }, { errors }, or {} to skip | | postMap | Import | After field mapping, before submit | options.preMapData[], postMapData[] | Array matching postMapData.length: { data }, { errors }, or {} to skip | | postSubmit | Import | After destination submission | options.preMapData[], postMapData[], responseData[] | responseData[] (same length, modified) | | postAggregate | Import | After file aggregation upload | options.postAggregateData: { success, _json, code, message } | void |

Record-Level Processors (on export or import)

| Hook | When | Input | Must return | |------|------|-------|-------------| | filter | Per-record, before processing | options.record | boolean (true = process) | | input_filter | Per-record on lookup exports | options.record | boolean (true = include) | | transform | Per-record, reshaping before mapping | options.record | Transformed record |

filter and transform have expression-based alternatives. Only use a script when the logic is too complex for an expression (multi-field conditionals, date math, external lookups).

Flow-Level Hook

| Hook | Runs on | When | Input | Must return | |------|---------|------|-------|-------------| | postResponseMap | Page processor (flow/API/tool) | After response mapping merges results | options.postResponseMapData[], responseData[] | postResponseMapData[] (same length) |

Configured on the flow's pageProcessors[] entry, not on the export/import. Plan this hook when building the resource, but wire it at the flow level.

Routing and Handlers

| Hook | Runs on | When | Input | Must return | |------|---------|------|-------|-------------| | branching | Router | Per-record routing decision | options.record, settings | number[] (branch indices, e.g., [0, 2]) | | handleRequest | API resource | Incoming HTTP request (script-mode API) | options.method, headers, queryString, body, rawBody | { statusCode, headers?, body } | | contentBasedFlowRouter | AS2 connection | EDI message routing | options.httpHeaders, mimeHeaders, rawMessageBody | { _flowId, _exportId } |

Quick Reference

Hook Point Decision Matrix

| When you need to... | Use hook | Configured on | Input / Output | |---|---|---|---| | Transform or filter a batch after retrieval | preSavePage | Export | Receives pages of records, returns pages (with optional errors) | | Filter individual records before processing | filter | Export or import | Receives single record, returns boolean (true = keep) | | Filter records entering a lookup export | input_filter | Export (lookup) | Receives single record, returns boolean (true = include) | | Reshape records before mapping | transform | Export or import | Receives single record, returns transformed record | | Transform records before field mapping | preMap | Import | Receives unmapped records array, returns array (same length) | | Transform records after field mapping | postMap | Import | Receives pre-map + post-map arrays, returns array (same length) | | Process API responses after submission | postSubmit | Import | Receives pre-map, post-map, and response arrays, returns response array | | Handle results after file aggregation | postAggregate | Import (file) | Receives aggregation result, returns void | | Post-response processing (merge lookup/import results) | postResponseMap | Flow pageProcessors[] entry | Receives merged records + response data, returns merged records (same length) | | Route records to branches | branching | Router in flow/tool | Receives single record + settings, returns branch indices array | | Handle incoming HTTP requests (script-mode API) | handleRequest | API resource | Receives method, headers, query, body; returns { statusCode, headers?, body } | | Route EDI messages to flows | contentBasedFlowRouter | AS2 connection | Receives HTTP/MIME headers + raw body, returns { _flowId, _exportId } |

Minimum Required Fields

A script resource needs only two fields:

  • name -- descriptive name (convention: - - , e.g., "Salesforce - getBatchRecords - postResponseMap")
  • content -- the JavaScript source code as a string

See [references/schemas/request.yml](references/schemas/request.yml) for the full create/update schema.

Related Skills

  • [configuring-exports > Quick Reference](../configuring-exports/SKILL.md#quick-reference) -- export configuration, where preSavePage, filter, transform, and input_filter hooks are wired
  • [configuring-imports > Quick Reference](../configuring-imports/SKILL.md#quick-reference) -- import configuration, where preMap, postMap, postSubmit, and postAggregate hooks are wired
  • [building-flows > How to Build a Flow](../building-flows/SKILL.md#how-to-build-a-flow) -- flow construction, where postResponseMap and branching hooks are wired
  • [writing-handlebars > Quick Reference](../writing-handlebars/SKILL.md#quick-reference) -- Handlebars expressions for dynamic values in scripts and hook configurations

Common Options Available to All Hooks

Most hooks receive these context fields in options:

  • _flowId, _integrationId, _apiId, _parentIntegrationId -- execution context IDs
  • _exportId or _importId -- the step's resource ID
  • _connectionId -- the connection in use
  • settings -- custom settings in scope for the resource
  • testMode -- boolean, whether running in test/preview mode
  • job -- the current job object

How to Write a Script

1. Determine what you need to accomplish

Map your goal to the right hook point using the [Hook Point Decision Matrix](#hook-point-decision-matrix) above.

2. Check if an expression can handle it

Filter, transform, and output filter all have expression-based alternatives. Expressions are simpler to maintain and don't require a script resource. Use a script only when you need:

  • Multi-step logic or loops
  • Cross-record calculations (totals, deduplication)
  • External API calls via integrator-api
  • Error handling with retry data
  • Access to preMapData alongside postMapData

3. Check for existing scripts in the account

celigo scripts list
celigo scripts get    # content is only returned on individual GET

4. Create the script resource

Build the script with the correct function name matching the hook point. A single script can contain multiple functions.

See [references/schemas/request.yml](references/schemas/request.yml) for the create/update schema and [references/schemas/response.yml](references/schemas/response.yml) for the response shape.

Key fields:

  • name -- descriptive name (convention: - - , e.g., "Salesforce - getBatchRecords - postResponseMap")
  • content -- the JavaScript source code

5. Wire the script to the resource

Wiring depends on the hook type:

| Hook | Wiring pattern | Where | |------|---------------|-------| | preSavePage, preMap, postMap, postSubmit, postAggregate | hooks.{hookType}: { _scriptId, function } | Export or import resource | | filter, input_filter, transform | {field}: { type: "script", script: { _scriptId, function } } | Export or import resource | | postResponseMap | hooks.postResponseMap: { _scriptId, function } | Flow pageProcessors[] entry | | branching | routeRecordsUsing: "script" + script reference | Router in flow | | handleRequest | script: { _scriptId, function } + type: "script" | API resource | | contentBasedFlowRouter | as2.contentBasedFlowRouter: { _scriptId, function } | AS2 connection |

Hook-based attachment (preSavePage, preMap, etc.) is additive -- adding a hook doesn't remove existing config. Replace-based attachment (filter, transform) replaces the existing filter/transform expression.

6. Test and iterate

# Enable debug logging on the script
celigo scripts enable-debug 

# Run the flow or API that triggers the script
celigo flows run  -y

# Check debug logs
celigo scripts debug-logs  --since 30

# Check execution logs
celigo scripts debug-logs  --level error --limit 20

# Disable debug when done
celigo scripts disable-debug 

Available Modules

Scripts can import three built-in modules:

integrator-api

Call Celigo APIs from within the script -- run exports, read connections, trigger imports.

import { exports, imports, connections } from 'integrator-api'

const result = exports.run({ _id: 'exportId' })
const conn = connections.get({ _id: 'connectionId' })

Useful in preSavePage for enrichment, handleRequest for orchestration, and postSubmit for triggering downstream processes.

dayjs

Date and time manipulation. Handles parsing, formatting, diffing, and timezone conversions without manual date math.

import dayjs from 'dayjs'

const formatted = dayjs(record.createdAt).format('YYYY-MM-DD')
const isRecent = dayjs().diff(dayjs(record.updatedAt), 'day') 
celigo scripts create   name="New Name"
celigo scripts delete 

Logs and Debugging

celigo scripts debug-logs  [--limit N] [--offset N] [--level error|warn|info|debug] [--start-date ISO] [--end-date ISO]
celigo scripts enable-debug  [--duration ]
celigo scripts disable-debug 
celigo scripts debug-logs  [--since ] [--flow-id ]

Pre-Submit Checklist

Before creating or updating a script, verify:

  • [ ] Hook point is correct -- the function name matches the hook type being wired (e.g., preSavePage function for a hooks.preSavePage reference)
  • [ ] Return value matches contract -- batch hooks (preMap, postMap, postSubmit, postResponseMap) return arrays that match the input array length exactly
  • [ ] Error handling uses return pattern, not throw -- per-record errors use { errors: [...] } return values, not thrown exceptions (which fail the entire page)
  • [ ] Expression alternative considered -- filter, transform, and output filter can use expressions; only use a script when expressions cannot handle the logic
  • [ ] content field is included on PUT -- omitting content on update erases the code; always GET first, modify, then PUT
  • [ ] Debug mode is disabled after testing -- celigo scripts disable-debug to avoid log noise in production

Gotchas

  1. Array length contracts are strict. preMap, postMap, and postResponseMap return arrays MUST match the input array length. Returning fewer or more elements fails the entire page silently or with cryptic errors.
  2. abort: true stops pagination, not the flow. In preSavePage, setting abort: true tells the export to stop generating new pages. It does NOT stop the flow or cancel processing of the current page's records.
  3. Script content is not returned in list responses. celigo scripts list shows metadata only. You must celigo scripts get to see the actual JavaScript code.
  4. PUT erases content if omitted. Always GET the script first, modify, then PUT the complete object. The set command handles this automatically.
  5. One script resource can contain multiple functions. A single script with both preSavePage and preMap functions can be wired to different resources by specifying the function name in each hook reference.
  6. Throwing an exception fails the entire page. In batch hooks (preSavePage, preMap, postMap, postSubmit), an unhandled exception fails ALL records on that page, not just one. Use the error return pattern ({ errors: [...] }) for per-record errors.
  7. postResponseMap lives on the flow, not the resource. The hook is configured on the pageProcessors[] entry in the flow/API/tool, even though it processes export or import response data.
  8. filter/transform scripts replace expression-based alternatives. Wiring a script filter replaces any existing expression filter. They cannot coexist on the same resource.
  9. console.log() output goes to script logs, not stdout. Use celigo scripts debug-logs to see output. Logs require debug mode to be enabled for debug-level messages.

Common Errors

| Error / Symptom | Cause | Fix | |---|---|---| | "The number of elements in the return value must match the input" | Batch hook return array length differs from input | Ensure return array has exactly data.length (preMap) or postMapData.length (postMap) elements; use {} for skipped records | | All records on a page fail with no per-record detail | Unhandled exception thrown in batch hook | Wrap logic in try/catch; return { errors: [...] } per record instead of throwing | | Script content is empty after update | PUT omitted the content field | Always GET first, modify, then PUT the complete object (or use celigo scripts set) | | abort: true set but flow keeps running | abort only stops pagination; current page still processes | This is expected behavior; use error returns or filter to skip individual records | | Script not executing / no logs | Script not wired to any resource, or debug mode not enabled | Verify _scriptId + function reference on the export/import/flow; enable debug with celigo scripts enable-debug | | "Function not found" or similar | function name in hook reference doesn't match an exported function in the script | Check the function name matches exactly (case-sensitive) between the hook config and the script's export | | Filter always returns all/no records | Filter function returns truthy/falsy value instead of strict boolean | Return explicit true or false; avoid returning objects or undefined | | postResponseMap not firing | Hook wired on the import/export instead of the flow's pageProcessors[] entry | Move the hook config to the pageProcessors[] entry in the flow, not the resource |

Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet, be the first.

Versions

  • v0.1.0 Imported from the upstream source.