PackagePackagetypeA library you install and call yourself.See every one →What this type is →by SAPby SAPoriginPublished by SAP itself. A visible facet, never an input to a reading — a dormant official plugin reads exactly like a dormant independent one.See every one →

com.sap.cds:cds-feature-n8n

Triggers n8n workflows from a CAP Java application through @n8n.process.start annotations or a programmatic N8nService, delivered by webhook calls through the CAP outbox.

Fires an HTTP request at an n8n webhook when a CDS event happens: annotate an entity or action with @n8n.process.start and the plugin fires the request when the annotated event occurs, posting the entity's fields as JSON, or call N8nService.trigger() from a handler for full control over payload and method. The webhook base URL is set directly in application.yaml, resolved from a BTP destination through the SAP Cloud SDK for production deployments behind a proxy, or left to a development-only localhost fallback that throws at startup in any other profile. Delivery runs through the CAP persistent outbox with configurable retry, retrying only network-level failures; a console mode logs calls instead of sending them, for local development and CI environments without a running n8n instance. It is the CAP Java counterpart to @cap-js/n8n; the Maven coordinate is com.sap.cds:cds-feature-n8n, at 0.0.1.

Facts

Last pushLicenceApache-2.0

Where it sits

Lives in
Provides
Workflow integrationCAPWorkflow integrationcapabilityDriving an external workflow or automation engine from an application and reacting to what it emits — triggering runs transactionally and importing the engine’s shape as a typed model.
Flavors
Optional
SAP Destination serviceSAP Destination serviceartifactn8n.destination is configured, for production deployments behind a proxy instead of a plain base URL.webhook endpoint resolution

Who is behind it

CAPCAPOrganisationMaintains this
In the atlas: maintains 10
@cap-java
maintainerHristina IvanovaPersonContributes to this
In the atlas: contributes to 1
@ivanovahr
Lisa Julia NebelPersonContributes to this
In the atlas: contributes to 15
@lisajulia
Samyuktha PrabhuPersonContributes to this
In the atlas: contributes to 4
@samyuktaprabhu
SAP Open Source Admin BotPersonContributes to this
In the atlas: contributes to 2
@SAP-OSPO-ADMIN
4 contributors

What it declares

Every artifact

Type
Package
Origin
By SAP
Runtime
Java

As a package

Where it runs
Inside its host
Distributed through
Maven

Connections2

Where it sits in your app

Download
Your CAP applicationYour SAP BTP subaccountdocumentedmay needif n8n.destination isconfigured, for productiondeployments behind a proxy inste…com.sap.cds:cds-feature-n8n, modulecom.sap.cds:cds-feat…SAP Destination service, service instanceSAP Destinationservice
SAPnot SAP's, or the project's ownunder a condition

README

quoted from cap-java/cds-feature-n8n under Apache-2.0, read
cap-java/cds-feature-n8n/README.mdOn GitHub

SAP Cloud Application Programming Model, n8n plugin for Java

REUSE status

CAP Plugin to automatically trigger and interact with n8n workflow automation tool - declaratively via @n8n.process.start annotations and programmatically via the n8n service.

Table of Contents

About this project

cds-feature-n8n is a Spring Boot auto-configuration plugin for CAP Java applications. It listens to CDS events (CREATE, DELETE, and custom actions) annotated with @n8n.process.start and fires HTTP requests to configured n8n webhook URLs — enabling you to trigger n8n workflows directly from your CAP service layer.

Features:

  • Annotation-driven: no boilerplate code needed in your service handlers
  • Supports entity CRUD events (CREATE, DELETE) and custom actions/functions
  • Configurable HTTP method per trigger (GET, POST, PUT, PATCH, DELETE, HEAD) — defaults to POST
  • Reliable delivery via CAP persistent outbox with configurable retry
  • Optional API key header (X-N8N-API-KEY) for authentication

Requirements

  • Java 21+
  • CAP Java 5+
  • Spring Boot 4+
  • A running n8n instance

Quick Start

Follow these steps to get a fully working local environment from scratch.

  1. Build and install the plugin locally:
mvn clean install
  1. Add the dependency to your CAP Java application's pom.xml:
<dependency>
    <groupId>com.sap.cds</groupId>
    <artifactId>cds-feature-n8n</artifactId>
    <version>0.0.1</version>
</dependency>
  1. Annotate an entity in your CDS model:
annotate AdminService.Books with @n8n.process.start: [
  {on: 'DELETE', path: 'book-deleted'}
];
  1. Configure the n8n base URL in application.yaml:
n8n:
  base-url: ${N8N_BASE_URL:http://localhost:5678}
  api-key: ${N8N_API_KEY:}
  1. Start the sample app
cd samples/bookshop/srv
mvn spring-boot:run

The app starts on http://localhost:8080 and points at http://localhost:5678/webhook by default. Any annotated CDS event will fire a webhook to your local n8n instance.

  1. Smoke test

    1. In n8n, create a workflow with a Webhook node, path book-deleted, save (Cmd+S), click "Listen for Test Event"
    2. Delete any book at http://localhost:8080 → Admin → Books
    3. n8n should show a green execution with { "ID": "...", "title": "...", "author_ID": "..." }

Alternative (test mode): If you prefer one-shot manual testing, set n8n.use-test-webhook: true in application.yaml, restart the app, then click "Listen for Test Event" in n8n instead of activating the workflow.

Connect to n8n

Local n8n
docker volume create n8n_data

docker run -it --rm \
  --name n8n \
  -p 5678:5678 \
  -e GENERIC_TIMEZONE="Europe/Berlin" \
  -e TZ="Europe/Berlin" \
  -e N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS=true \
  -e N8N_RUNNERS_ENABLED=true \
  -v n8n_data:/home/node/.n8n \
  docker.n8n.io/n8nio/n8n

Replace Europe/Berlin with your local timezone (e.g. America/New_York). The named volume n8n_data persists your workflows across container restarts. n8n will be available at http://localhost:5678.

First run only: open http://localhost:5678 in a browser and create an owner account before proceeding.

Environment Variables

Configure the n8n base URL and optional API key in application.yaml:

n8n:
  base-url: ${N8N_BASE_URL:http://localhost:5678}
  api-key: ${N8N_API_KEY:}

N8N_API_KEY is sent as X-N8N-API-KEY on every webhook request — this is the same header n8n uses for its public REST API. Set N8N_API_KEY in your environment (or ~/.zshrc) and configure the n8n Webhook node with Authentication: Header Auth, Name: X-N8N-API-KEY, Value: same string.

Without it, n8n must have Authentication: None — otherwise it returns 403.

BTP Destination

For production deployments — especially SAP managed n8n instances behind a proxy — you can configure a BTP destination instead of a plain base URL. The destination takes priority over base-url:

n8n:
  destination: my-n8n-dest   # BTP destination name (takes priority over base-url)
  api-key: ${N8N_API_KEY:}   # optional; sent as X-N8N-API-KEY in addition to any proxy auth

When destination is set, the plugin resolves it via the SAP Cloud SDK at startup. The destination's URL and auth headers (e.g. Authorization: Bearer … for OAuth2 destinations) are merged into every request. X-N8N-API-KEY is then added on top — so both the outer proxy auth and the n8n-level API key are sent.

The destination can also carry the API key as a custom property (URL.headers.X-N8N-API-KEY) instead of setting n8n.api-key — though n8n.api-key takes precedence if both are set.

To use destinations, add cloudplatform-connectivity to your application's dependencies:

<dependency>
  <groupId>com.sap.cloud.sdk.cloudplatform</groupId>
  <artifactId>cloudplatform-connectivity</artifactId>
</dependency>
Credential Resolution

The plugin resolves connection details in this order:

  1. n8n.use-console: true — console mode, takes precedence over all other configuration (see Console Mode)
  2. n8n.destination — BTP destination (takes priority over base-url)
  3. n8n.base-url — explicit base URL in application.yaml
  4. Dev-only fallback: http://localhost:5678 (development profile only — throws at startup in any other profile)

n8n.api-key is independent of the above and is always sent as X-N8N-API-KEY when set.

Webhook Requests

Test Webhooks

The path value in each annotation is appended after /webhook to form the full webhook URL:

path: 'book-deleted' → http://localhost:5678/webhook/book-deleted

Test vs. production webhooks

Moden8n URLWhen to use
Production (default)/webhookWorkflows are active and handle every call
Test/webhook-testOne-off manual testing; requires clicking "Listen for Test Event" in the n8n UI each time, and cannot handle bulk calls

Toggle test mode via use-test-webhook in application.yaml:

n8n:
  use-test-webhook: true   # set to false (default) for production webhooks

Annotations

Annotate entities or actions in your CDS model with @n8n.process.start. No additional Java code is needed — the plugin detects the annotation and fires the webhook automatically.

Triggering a workflow

Each trigger entry supports the following properties:

PropertyRequiredDescription
onyesEvent name — CREATE, READ, UPDATE, DELETE, or the action name
pathyesAppended to n8n.base-url + /webhook (or /webhook-test if use-test-webhook: true in application.yaml) to form the full webhook URL
methodnoHTTP method to use when calling the n8n webhook — one of GET, POST, PUT, PATCH, DELETE, HEAD. Defaults to POST. Must match the method configured on the n8n Webhook node.
inputsnoFields to include in the payload; defaults to all direct entity attributes when omitted

Entity events (CRUD):

annotate AdminService.Books with @n8n.process.start: [
  {on: 'DELETE', path: 'book-deleted', inputs: [$self.ID, $self.title, $self.stock]},
  {on: 'UPDATE', path: 'book-updated', method: 'PUT', inputs: [$self.ID, $self.title]}
];

An invalid value (e.g. method: 'YOLO') causes the application to fail at startup with a descriptive error:

IllegalStateException: @n8n.process.start[0] on entity 'MyService.Books' has invalid 'method' value 'YOLO'.
Allowed values: GET, POST, PUT, PATCH, DELETE, HEAD.

Note: GET sends no request body — the payload is serialized as query parameters instead. All other methods send a JSON body.

For custom actions:

annotate CatalogService.submitOrder with @n8n.process.start.on: 'submitOrder'
                                        @n8n.process.start.path: 'order-submitted';

When the annotated event fires, the plugin posts the selected inputs fields as a flat JSON object to the configured webhook URL. For example, with the inputs list above:

{
  "ID": "abc123",
  "title": "The Hobbit",
  "stock": 42
}

When inputs is omitted, all direct fields of the entity are included in the payload. Specify inputs explicitly to limit which fields are sent — useful to avoid exposing sensitive or large fields.

Association fields can be included using dot notation — the plugin issues a single expanded query to fetch the associated data:

annotate AdminService.Books with @n8n.process.start: [
  {on: 'DELETE', path: 'book-deleted', inputs: [$self.ID, $self.title, $self.author.name]}
];

This produces a payload with the leaf field name as the key:

{
  "ID": "abc123",
  "title": "The Hobbit",
  "name": "Tolkien"
}

Note:

  1. Only one level of association traversal is supported ($self.author.name). Deeper paths ($self.author.address.city) are skipped with a warning. This is a known limitation compared to the Node.js plugin — contributions welcome.
  2. Association paths are not resolved for CREATE events. For these, the plugin uses the raw request payload (the data as submitted), so association fields like $self.author.name will be null. Use direct FK fields (e.g. $self.author_ID) for CREATE triggers instead.
Conditions

Add an if expression to a trigger entry to fire the webhook only when the condition is met:

annotate AdminService.Books with @n8n.process.start: [
  {on: 'DELETE', path: 'book-deleted',   if: (stock = 0), inputs: [$self.ID, $self.title, $self.author_ID]},
  {on: 'UPDATE', path: 'book-updated',   inputs: [$self.ID, $self.title]},
  {on: 'UPDATE', path: 'book-low-stock', inputs: [$self.ID, $self.title, $self.stock],
                                          if: (stock < 10)}
];

Only deletes where stock is 0 fire book-deleted; every update fires book-updated; only updates that bring stock below 10 also fire book-low-stock — e.g. to trigger a reorder workflow in n8n.

The if property is optional. When present, the webhook fires only when the condition evaluates to true against the entity row:

PropertyRequiredDescription
ifnoCDS expression — webhook fires only when the condition is true

Supported operators: =, ==, !=, <>, <, <=, >, >=, in, like, between, is null, is not null, not, and, or.

Note: The if condition is evaluated in application code against the entity row available at the time the event fires — it is not pushed to the database. This means it uses the same data the plugin already has: the CQN payload for CREATE, and the prefetched row for UPDATE and DELETE. Complex expressions involving subqueries or navigation paths that aren't part of the prefetched columns will not work.

Multiple Triggers

Multiple trigger entries for the same event on the same entity are supported — all matching entries fire. This allows you to route to different n8n workflows from one event, optionally with different if conditions.

Programmatic API

Trigger a Workflow

For cases where you need full control over when and what is sent, inject N8nService directly into any CAP event handler and call .trigger():

@Component
@ServiceName(AdminService_.CDS_NAME)
public class AdminServiceHandler implements EventHandler {

    @Autowired
    private N8nService n8nService;

    @After(event = CqnService.EVENT_CREATE, entity = "AdminService.Books")
    public void afterCreateBook(List<Books> books) {
        books.forEach(book -> n8nService.trigger("book-created", Map.of(
            "ID", book.getId(),
            "title", book.getTitle()
        )));  // defaults to POST when no HTTP Method is specified
    }
}

The first argument to .trigger() is the webhook path — appended after /webhook to form the full URL (e.g. "book-created" → http://localhost:5678/webhook/book-created). The second argument is the payload — any Map<String, Object> you choose to send. The third argument is the HTTP method — pass POST, PUT, PATCH, DELETE, GET, or HEAD. It must match the method configured on the n8n Webhook node. If you omit the third argument, POST is used by default.

Note: The annotation-based and programmatic approaches are independent. You can use both in the same application, but take care not to fire duplicate webhooks for the same event.

Delivery Behaviour

The plugin retries failed webhook calls only on network-level errors — when n8n is unreachable (connection refused, timeout). HTTP error responses are not retried:

ResponseMeaningRetried?
Network error / timeoutn8n is down or unreachableYes
5xxn8n responded but the workflow itself failedNo
4xxMisconfiguration (wrong URL, bad auth)No

Retry behavior is managed by the CAP persistent outbox. Configure it under cds.outbox.services.N8nOutbox in your application.yaml:

cds:
  outbox:
    services:
      N8nOutbox:
        maxAttempts: 10   # total attempts before the message is marked as failed
        ordered: true     # process messages in submission order (default: true)
Console Mode

The plugin ships a built-in console mode for local development and CI environments where no n8n instance is available. When enabled, webhook calls are logged instead of sent via the specified HTTP Method — the app behaves normally but never makes an HTTP request to n8n. Delivery is synchronous and skips the persistent outbox entirely, so no rows are written to cds_outbox_Messages.

n8n:
  use-console: true

No base-url is needed. Console mode takes precedence over all other configuration. You'll see:

INFO ConsoleN8NWebhookService - [console-n8n-service]: would POST /webhook/book-deleted

Inject ConsoleN8NWebhookService to assert on webhook calls without a real n8n instance:

@SpringBootTest
@TestPropertySource(properties = "n8n.use-console=true")
class MyServiceTest {

    @Autowired
    ConsoleN8NWebhookService consoleWebhookService;

    @Test
    void deleteBook_triggersWebhook() {
        // ... trigger a delete ...

        assertThat(consoleWebhookService.getExecutions()).hasSize(1);
        Map<String, Object> exec = consoleWebhookService.getExecutions().get(0);
        assertThat(exec.get("path")).isEqualTo("book-deleted");
        assertThat(exec.get("status")).isEqualTo("success");
    }
}

Each execution record contains: id, path, method, payload, startedAt, finishedAt, status.

If n8n.use-console is false (the default) and n8n.base-url is not set:

ProfileBehaviour
developmentWarns at startup and falls back to http://localhost:5678. HTTP calls fail gracefully if n8n is not running — the outbox retries with backoff.
any otherThrows IllegalStateException at startup: set N8N_BASE_URL or use n8n.use-console=true.

Tests

Run the unit tests from the project root:

mvn test

To also generate a JaCoCo coverage report (unit tests only):

mvn verify

To include the retry integration test in coverage:

mvn verify -pl cds-feature-n8n -am -Dtest="N8nHandlerTest,N8nWebhookServiceRetryIT"

The report is written to cds-feature-n8n/target/site/jacoco/index.html.

Integration tests

The retry integration test (N8nWebhookServiceRetryIT) is excluded from the default mvn test run because Maven Surefire skips *IT.java classes by default. Run it explicitly:

mvn test -pl cds-feature-n8n -am -Dtest=N8nWebhookServiceRetryIT

This test uses WireMock to start a local HTTP server that stands in for n8n, verifying that the retry logic fires the webhook up to three times before giving up.

Testing with the sample application

The samples/bookshop directory contains a complete CAP bookshop app that demonstrates the plugin with real webhook triggers.

For a full walkthrough including starting n8n locally in Docker, see Local n8n.

Quick start (assumes n8n is already running):

cd samples/bookshop/srv
mvn spring-boot:run

The sample configures three webhook triggers on AdminService.Books: DELETE with if: (stock = 0) fires book-deleted; every UPDATE fires book-updated; and updates that bring stock below 10 also fire book-low-stock. See Local n8n for the full walkthrough. 404 → listener expired or workflow not saved. 403 → X-N8N-API-KEY mismatch.

Support, Feedback, Contributing

This project is open to feature requests/suggestions, bug reports etc. via GitHub issues. Contribution and feedback are encouraged and always welcome. For more information about how to contribute, the project structure, as well as additional contribution information, see our Contribution Guidelines.

Security / Disclosure

If you find any bug that may be a security problem, please follow our instructions at in our security policy on how to report it. Please do not create GitHub issues for security-related doubts or problems.

Code of Conduct

We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone. By participating in this project, you agree to abide by its Code of Conduct at all times.

Licensing

Copyright 2026 SAP SE or an SAP affiliate company and cds-feature-n8n contributors. Please see our LICENSE for copyright and license information. Detailed information including third-party components and their licensing/copyright information is available via the REUSE tool.

Apache-2.0, read

History

Latest

v0.0.2

Changed 3

Notes
Changed
  • Webhook authentication is now configurable via n8n.webhook-auth.* — supports basic (username + password), header (custom header name and value), and bearer (token) auth types, matching the three auth options of the n8n Webhook node
  • X-N8N-API-KEY is no longer forwarded to webhook nodes; it is reserved for future n8n REST API calls (/api/v1/…)
  • BTP destination path: X-N8N-API-KEY is filtered from destination headers before forwarding to webhook nodes; n8n.webhook-auth is merged on top
  1. v0.0.1

    Features 12

    Notes
    Added
    • Annotation-driven n8n webhook triggers via @n8n.process.start — no boilerplate service handler code required
    • Supports CDS entity events (CREATE, READ, UPDATE, DELETE) and custom actions/functions as trigger sources
    • Configurable HTTP method per trigger (GET, POST, PUT, PATCH, DELETE, HEAD) via the method property — defaults to POST
    • Reliable webhook delivery via CAP persistent outbox with configurable retry
    • Webhook authentication via X-N8N-API-KEY header, configured through n8n.api-key / N8N_API_KEY
    • BTP destination support: configure n8n.destination to resolve the n8n base URL and authentication headers from the BTP Destination Service — no redeployment needed when the n8n instance changes
    • Optional inputs payload selection on @n8n.process.start; when omitted, all direct entity fields are sent automatically
    • Association path inputs (e.g. $self.category.name) for including resolved to-one association fields in the webhook payload
    • ConsoleN8NWebhookService: offline/test mode that logs webhook calls instead of making HTTP requests — enable with n8n.use-console=true
    • Profile-aware startup validation: warns and falls back to http://localhost:5678 in the development profile; fails fast in all other profiles when n8n.base-url is not set
    • n8n.base-url expects the bare n8n host (e.g. http://localhost:5678) — the plugin appends /webhook automatically, or /webhook-test when n8n.use-test-webhook=true
    • Programmatic API via the n8n service for triggering workflows from custom handler code
Workflow integration
  1. @cap-js/n8nTriggers n8n workflows from a CAP application
  2. @cap-js/processStarts and manages SAP Build Process Automation processes from CAP
  3. cap-n8nIntegration of CAP with n8n workflows and triggers

Details

Sources3
Added by handdiscovered herechecked
github.com/cap-java/cds-feature-n8ntree, pushes, contributors, CIchecked
README.mdquoted, and what the entry declareschecked

Sources are graph entities: the same page may back several entries, and an unreachable source flags every row that rests on it.

Compatibility and provenance
runtimesjava
discovered viaAdded by hand
first seen in the atlas
ecosystem and domaincap, process-and-workflow
Flavors1
Java
“cds-feature-n8n is a Spring Boot auto-configuration plugin for CAP Java applications.” — cap-java/cds-feature-n8n — READMEchecked
Contributors4

From the repository’s contributor list, .

History in the atlas2
  1. Fires an HTTP request at an n8n webhook when a CDS event or action annotated with @n8n.process.start occurs, or through a programmatic N8nService.trigger() call, with delivery through the CAP persistent outbox and retry limited to network-level failures.
  2. First seen.
For your agent
Ref
artifact:cap-java-cds-feature-n8n
JSON
/artifacts/cap-java-cds-feature-n8n.json
MCP
Connecting an agent