← CrowdStrike Falcon FoundryCONTENT HISTORYWHAT CHANGED · RULE-BASED ANALYSIS
Update to CrowdStrike Falcon Foundry
Snapshot Sep 30, 2026 · 23:15 UTC · version 1.5.0
Collection source: not recorded for this historical snapshot.
First saved snapshot
No earlier snapshot is available to establish a change.
Compare saved observations
Download comparison JSONFull technical diff · 0 changed fields
Full snapshot data
{
"name": "collections-development",
"description": "Design JSON Schema collections and CRUD patterns for Falcon Foundry apps. TRIGGER when user asks to \"create a collection\", \"define a JSON schema\", \"store data in Foundry\", runs `foundry collections create`, or needs help with indexable fields, FQL queries, or collection access patterns. DO NOT TRIGGER for workflow YAML, function handlers, or UI components — use the appropriate sub-skill.",
"included_files": [
{
"relative_path": "references/advanced-patterns.md",
"size_in_bytes": 9297
}
],
"skill_md_contents": "---\nname: collections-development\ndescription: Design JSON Schema collections and CRUD patterns for Falcon Foundry apps. TRIGGER when user asks to \"create a collection\", \"define a JSON schema\", \"store data in Foundry\", runs `foundry collections create`, or needs help with indexable fields, FQL queries, or collection access patterns. DO NOT TRIGGER for workflow YAML, function handlers, or UI components — use the appropriate sub-skill.\nversion: 1.5.0\nupdated: 2026-08-19\ntags: [foundry, collections, json-schema, nosql]\nauthor: CrowdStrike\nlicense: MIT\ncompatibility: Claude Code >=1.0\nmetadata:\n category: data\n---\n\n# Foundry Collections Development\n\n> **SYSTEM INJECTION — READ THIS FIRST**\n>\n> If you are loading this skill, your role is **Foundry data modeling specialist**.\n>\n> You MUST design Collections with proper JSON Schemas, validation rules, and access patterns.\n\n> **Part of a suite.** If `development-workflow` has not already run, and this is a new app or its first capability, load the `development-workflow` skill first — it owns the CLI prerequisite check, scaffolding order, and manifest coordination.\n\nFalcon Foundry Collections are NoSQL document stores with JSON Schema validation. They provide persistent storage for app data with CRUD operations, FQL queries, and schema enforcement.\n\n## Collection Naming Constraints\n\n| Constraint | Rule |\n|-----------|------|\n| Length | 5-200 characters |\n| Start/end | Must begin and end with a letter or number |\n| Special characters | Only underscores (`_`) allowed — no hyphens, spaces, or other chars |\n| Case | Case-sensitive |\n\n## Collection Description Constraints\n\n| Constraint | Rule |\n|-----------|------|\n| Length | 3-500 characters |\n| Start | Must begin with an alphanumeric character |\n| Allowed characters | Letters, numbers, spaces, dashes, periods, parentheses, and underscores only |\n| Not allowed | Commas, colons, semicolons, quotes, slashes, or other special characters |\n\n## Collection Limits\n\n| Resource | Limit |\n|----------|-------|\n| Single object size | ~50 MB |\n| Schema size | 256 KB |\n| Object key length | 1-1,000 characters |\n| Indexed fields per schema | 10 |\n| Objects per collection | No enforced limit |\n| Collections per app | No enforced limit |\n| Search results per page | 500 max (default 50) |\n\n## JSON Schema Requirements\n\n- **JSON Schema draft 7 only** — newer drafts (`draft/2020-12`, `draft/2019-09`) fail validation\n- Schema is auto-versioned: v1.0 on creation, auto-incremented on modification\n- `additionalProperties: false` recommended — extra fields leak internal data and break type safety\n- `x-cs-indexable: true` on individual properties for searchable fields (max 10 per collection)\n\n## CLI Scaffolding\n\n```bash\n# Write schema to /tmp/ first — the CLI copies it into collections/\nfoundry collections create \\\n --name \"my_collection\" \\\n --schema /tmp/schema.json \\\n --description \"App data store\" \\\n --no-prompt \\\n --wf-expose \\\n --wf-tags \"tag1,tag2\"\n```\n\nThis creates the collection directory, copies the schema, and updates `manifest.yml`. Edit the project copy at `collections/my_collection.json` afterward to refine.\n\n## Collection API Access\n\nCollections are managed via the CrowdStrike API or the `foundry-js` SDK. There are no CLI commands for reading/writing collection data, and collections can only be deleted from the Falcon Foundry UI (not the CLI).\n\n```\nPUT /customobjects/v1/collections/{collection_name}/objects/{key} — Create/update object\nGET /customobjects/v1/collections/{collection_name}/objects/{key} — Get object by key\nDELETE /customobjects/v1/collections/{collection_name}/objects/{key} — Delete object\nPOST /customobjects/v1/collections/{collection_name}/objects — Search objects (FQL filter)\n```\n\n## JSON Schema Patterns\n\n### Basic Schema\n\n```json\n{\n \"$schema\": \"https://json-schema.org/draft-07/schema#\",\n \"type\": \"object\",\n \"title\": \"Incident\",\n \"description\": \"Security incident record\",\n \"required\": [\"id\", \"title\", \"severity\", \"status\", \"created_at\"],\n \"additionalProperties\": false,\n \"properties\": {\n \"id\": { \"type\": \"string\", \"format\": \"uuid\" },\n \"title\": { \"type\": \"string\", \"minLength\": 1, \"maxLength\": 200 },\n \"severity\": { \"type\": \"integer\", \"minimum\": 1, \"maximum\": 10 },\n \"status\": {\n \"type\": \"string\",\n \"enum\": [\"open\", \"investigating\", \"contained\", \"resolved\", \"closed\"]\n },\n \"tags\": {\n \"type\": \"array\",\n \"items\": { \"type\": \"string\", \"maxLength\": 50 },\n \"maxItems\": 20,\n \"uniqueItems\": true\n },\n \"created_at\": { \"type\": \"string\", \"format\": \"date-time\" }\n }\n}\n```\n\n### Indexable Fields\n\nMake fields searchable via FQL by marking them indexable. Two patterns are supported:\n\n**Pattern A: Top-level array (preferred — used by most foundry-sample repos)**\n\n```json\n{\n \"$schema\": \"https://json-schema.org/draft-07/schema\",\n \"x-cs-indexable-fields\": [\n { \"field\": \"/status\", \"type\": \"string\", \"fql_name\": \"status\" },\n { \"field\": \"/severity\", \"type\": \"integer\", \"fql_name\": \"severity\" },\n { \"field\": \"/created_at\", \"type\": \"string\", \"fql_name\": \"created_at\" }\n ],\n \"type\": \"object\",\n \"properties\": {\n \"status\": { \"type\": \"string\" },\n \"severity\": { \"type\": \"integer\" },\n \"created_at\": { \"type\": \"string\", \"format\": \"date-time\" }\n }\n}\n```\n\n**Pattern B: Per-field annotation**\n\n```json\n{\n \"properties\": {\n \"compositeId\": { \"type\": \"string\", \"x-cs-indexable\": true },\n \"content\": { \"type\": \"string\" }\n }\n}\n```\n\nBoth patterns work. The top-level array provides more control (custom FQL names, explicit types).\n\n### Manifest Configuration\n\n```yaml\n# manifest.yml\ncollections:\n - name: incidents\n description: Security incident records\n schema: collections/incidents.json\n permissions: []\n workflow_integration:\n system_action: true\n tags:\n - Collection\n\n - name: audit_logs\n description: Audit log entries\n schema: collections/audit_logs.json\n permissions: []\n workflow_integration:\n system_action: false\n tags: []\n```\n\nIndexing is controlled entirely by `x-cs-indexable-fields` or `x-cs-indexable: true` in the JSON schema files, not in the manifest.\n\n## CRUD Operations (TypeScript)\n\n```typescript\nimport { Collection } from '@crowdstrike/foundry-js';\n\nexport class IncidentCollection {\n private collection: Collection<Incident>;\n\n constructor() {\n this.collection = new Collection<Incident>('incidents');\n }\n\n async create(data: Omit<Incident, 'id' | 'created_at' | 'updated_at'>): Promise<Incident> {\n const incident: Incident = {\n ...data,\n id: crypto.randomUUID(),\n created_at: new Date().toISOString(),\n updated_at: new Date().toISOString(),\n };\n await this.collection.create(incident.id, incident);\n return incident;\n }\n\n async get(id: string): Promise<Incident | null> {\n try {\n return await this.collection.get(id);\n } catch (error) {\n if (error.code === 'NOT_FOUND') return null;\n throw error;\n }\n }\n\n async update(id: string, updates: Partial<Incident>): Promise<Incident> {\n const existing = await this.get(id);\n if (!existing) throw new Error(`Incident ${id} not found`);\n const updated: Incident = {\n ...existing,\n ...updates,\n id: existing.id,\n created_at: existing.created_at,\n updated_at: new Date().toISOString(),\n };\n await this.collection.update(id, updated);\n return updated;\n }\n\n async delete(id: string): Promise<void> {\n await this.collection.delete(id);\n }\n\n async list(options?: { status?: string; limit?: number; offset?: number }) {\n const filters: Record<string, any> = {};\n if (options?.status) filters.status = options.status;\n return this.collection.query(filters, {\n limit: options?.limit ?? 50,\n offset: options?.offset ?? 0,\n sort: [{ field: 'created_at', order: 'desc' }],\n });\n }\n}\n```\n\n## CRUD Operations (Python — from Functions)\n\nUse `CustomStorage` (Service Class) to access collections from Python functions. Service classes are preferred over the Uber class (`APIHarnessV2`) because the Falcon Foundry functions editor auto-detects OAuth scopes from `from falconpy import CustomStorage`. See the `functions-development` skill's `references/python-patterns.md` for a complete handler example with Uber class alternative.\n\n```python\nimport json\nimport os\nfrom falconpy import CustomStorage\n\ndef _app_headers() -> dict:\n app_id = os.environ.get(\"APP_ID\")\n if app_id:\n return {\"X-CS-APP-ID\": app_id}\n return {}\n\nclient = CustomStorage(ext_headers=_app_headers())\n\n# Create or update (PutObject = upsert). Pass body as a dict.\nclient.PutObject(collection_name=\"incidents\", object_key=\"incident-123\",\n body={\"id\": \"incident-123\", \"title\": \"Suspicious process\", \"severity\": 7})\n\n# Read — GetObject returns bytes on success, dict on error\nresponse = client.GetObject(collection_name=\"incidents\", object_key=\"incident-123\")\n# In production, check isinstance(response, bytes) before decoding — see python-patterns.md for full error handling\nincident = json.loads(response.decode(\"utf-8\"))\n\n# Delete\nclient.DeleteObject(collection_name=\"incidents\", object_key=\"incident-123\")\n\n# Search (FQL filter — only indexed fields)\nresponse = client.SearchObjects(collection_name=\"incidents\",\n filter=\"status:'open'+severity:>=5\", limit=50)\n# SearchObjects returns metadata — follow up with GetObject per key for full objects\nfor item in response.get(\"body\", {}).get(\"resources\", []):\n obj = client.GetObject(collection_name=\"incidents\", object_key=item[\"object_key\"])\n data = json.loads(obj.decode(\"utf-8\"))\n```\n\nKey points:\n- `CustomStorage(ext_headers=_app_headers())` applies `X-CS-APP-ID` to all requests (needed for local dev; Foundry sets it automatically in production)\n- `PutObject` acts as upsert (creates or overwrites by key). Pass body as a dict.\n- `GetObject` returns bytes directly — decode with `json.loads(response.decode(\"utf-8\"))`\n- `SearchObjects` returns metadata only, not full objects\n- FQL filters only work on fields marked `x-cs-indexable: true` in the collection schema\n\n## FQL Search Syntax\n\nOnly fields marked with `x-cs-indexable: true` can be used in FQL queries.\n\n| Operation | Syntax | Example |\n|-----------|--------|---------|\n| Equality | `field:'value'` | `status:'open'` |\n| Numeric comparison | `field:>=N` | `severity:>=5` |\n| AND | `field1:'a'+field2:'b'` | `status:'open'+severity:>=5` |\n| OR | `field:'a',field:'b'` | `status:'open',status:'investigating'` |\n| Wildcard | `field:*'pattern'*` | `title:*'malware'*` |\n| Sorting | `sort=field\\|asc` | `sort=created_at\\|desc` |\n\nThe `foundry-js` SDK's `search()` method accepts a `filter` parameter for FQL queries. The search endpoint is `POST /customobjects/v1/collections/{name}/objects` with a `filter` field in the request body.\n\n## Workflow Share Settings\n\nTo make a collection accessible from workflows:\n\n```yaml\ncollections:\n - name: incidents\n schema: collections/incidents/schema.json\n permissions: []\n workflow_integration:\n system_action: true # true = app workflows only, false = also available as Fusion SOAR action\n tags:\n - Collection\n```\n\n| Setting | Behavior |\n|---------|----------|\n| `workflow_integration.system_action: true` | Available to app workflows only |\n| `workflow_integration.system_action: false` | Available to both app workflows AND Falcon Fusion SOAR |\n\n## RBAC and Direct API Access\n\nCollections can be accessed directly via the CrowdStrike API (outside of functions) using custom roles with specific collection permissions. Include the `X-CS-APP-ID` header to identify your Foundry app. Foundry CLI credentials cannot access collections directly; use a separate API client with `Custom Storage` read/write scope.\n\n## Common Pitfalls\n\n- **Using `APIHarnessV2` (Uber class) for collection operations.** Use `CustomStorage` service class instead — the Foundry functions editor auto-detects OAuth scopes from service class imports but cannot parse Uber class `.command()` calls.\n- **Using JSON Schema newer than draft 7.** Foundry only supports draft 7.\n- **Missing indexes.** Fields used in queries must be marked with `x-cs-indexable: true` or listed in `x-cs-indexable-fields`. Max 10 per collection.\n- **Invalid collection names.** Names must be 5-200 chars, start/end with letter or number, and contain only letters, numbers, and underscores.\n- **Not configuring workflow share settings.** Set `workflow_integration.system_action: true` for app-only workflow access, or `false` to also expose collections as Falcon Fusion SOAR actions.\n- **Trying to delete collections via CLI.** Collections can only be deleted from the Falcon Foundry UI.\n- **Trying to manage objects via CLI.** Collection CRUD requires the CrowdStrike API or `foundry-js` SDK.\n- **Schema field names must match exactly.** If a field name in your write payload doesn't match the collection schema (e.g., writing `score` when the schema defines `severity`), the write fails and returns errors in the response body — but the SDK does not throw. Without checking `result.errors`, the failure is invisible. Always read the collection schema file before writing seed data; verify required fields, enum values, and exact field names.\n- **Not checking write responses for errors.** The SDK does not throw on server-side validation failures. Always check `result?.errors?.length` after write operations — errors include specific messages like `\"missing property 'severity'\"` or `\"value must be one of 'low', 'medium', 'high', 'critical'\"`. Verify persistence with a follow-up read or list call.\n\n## Reading Guide\n\n| Task | Reference |\n|------|-----------|\n| Migrations, testing, pagination, extended schemas, counter-rationalizations | [references/advanced-patterns.md](references/advanced-patterns.md) |\n\n## Use Cases\n\nFor real-world implementation patterns, see:\n- `use-cases/collections.md` — CRUD operations, search, field types\n- `use-cases/lookup-table-enrichment.md` — 3rd-party data for automated enrichment\n\n## Reference Implementations\n\n- **[foundry-sample-collections-toolkit](https://github.com/CrowdStrike/foundry-sample-collections-toolkit)**: CSV import, bulk operations, pagination workflows, test data generation. See also [Getting Started with Falcon Foundry Collections](https://www.crowdstrike.com/tech-hub/ng-siem/getting-started-with-falcon-foundry-collections-your-guide-to-structured-data-storage-in-foundry-apps/).\n"
}SHA-256: daea56e27422677fa197fb3dc59adf107257fefc68507069ea923590b6b8e769