|
First of all, thanks for the very helpful https://docs.xyops.io/#Docs/compare page! As an n8n user, I have one question though: I have a set of n8n workflows which get triggered by webhooks which can also receive parameters in n8n as well as return data. I kind of miss this in xycop's magiclink concept. Is this something you plan or does that not fit your strategy? |
Replies: 5 comments 9 replies
|
Hey @arminus, You can send data to Magic Links now. You can either pass the parameters through the Query Parameters in the URL on a GET request or pass them in the body as JSON in a POST request. Examples (PowerShell): GET Request Invoke-RestMethod -Uri 'https://xyops.domain.com/api/app/magic/v1/94XlHVdhN5j5wbW5J3Lu4eGBwZhqwerdyJCFjXZp268FWNoPJL2ryYs6goae0wNAF?firstname=John&lastname=Doe'POST Request Invoke-RestMethod -Uri 'https://xyops.domain.com/api/app/magic/v1/94XlHVdhN5j5wbW5J3Lu4eGBwZhqwerdyJCFjXZp268FWNoPJL2ryYs6goae0wNAF' -Method Post -Body (@{firstname = 'John'; lastname = 'Doe'} | ConvertTo-Json -Depth 100) -ContentType 'application/json'The results provide an id and stream fields. These can be used with the stream_job API to stream the job updates from the server. References: Though I haven't been able to find anything regarding the job just returning a custom result. There is a bit about setting a custom success result but it's not programmatic based on your script itself, but instead it's instant and the job just runs in the background. https://docs.xyops.io/#Docs/triggers/magic-link I hope this helps. It would be a good feature request to allow the job to return custom HTTP results at the end of the script run to allow returning of data. |
|
Ah, Nick covered this quite well. One additional detail I just want to add about returning data: the stream_job API actually includes the final job output in its SSE stream, including the job's custom output data. When the job completes, the final This means the calling application can keep the HTTP connection open, process updates as they arrive, and then use the Here is a small Node.js example which calls magic to launch the job, and then streams the job response with stream_job: const magicLinkUrl = 'https://xyops.example.com/api/app/magic/v1/YOUR_MAGIC_LINK_TOKEN';
// Add optional event parameters to the Magic Link URL.
const magicUrl = new URL(magicLinkUrl);
magicUrl.searchParams.set('firstname', 'John');
magicUrl.searchParams.set('lastname', 'Doe');
// Start the job.
const magicResponse = await fetch(magicUrl);
if (!magicResponse.ok) {
throw new Error(`Magic Link request failed: HTTP ${magicResponse.status}`);
}
const job = await magicResponse.json();
if (job.code !== 0) {
throw new Error(job.description || 'Failed to start job');
}
// Connect to the SSE stream using the returned job ID and stream token.
const streamUrl = new URL('/api/app/stream_job/v1', magicUrl.origin);
streamUrl.searchParams.set('id', job.id);
streamUrl.searchParams.set('token', job.stream);
const streamResponse = await fetch(streamUrl);
if (!streamResponse.ok) {
throw new Error(`Job stream failed: HTTP ${streamResponse.status}`);
}
const decoder = new TextDecoder();
let buffer = '';
let jobOutput;
for await (const chunk of streamResponse.body) {
buffer += decoder.decode(chunk, { stream: true });
const messages = buffer.split(/\r?\n\r?\n/);
buffer = messages.pop();
for (const message of messages) {
const event = message.match(/^event:\s*(.+)$/m)?.[1];
const json = message.match(/^data:\s*(.+)$/m)?.[1];
if (!json) continue;
const data = JSON.parse(json);
// Display each SSE update as it arrives.
console.log(event, data);
// The completed update contains the job's final output.
if (event === 'update' && Object.hasOwn(data, 'completed')) {
jobOutput = data.data;
}
}
}
console.log('Job output:', jobOutput);Hope this helps! |
|
Thanks a lot for the explanations and also the example! "My problem" is that this relies on the caller to implement its own async waiting loop, which is ok if the caller is implemented in code, but is a showstopper if called from something like https://grafana.com/grafana/plugins/yesoreyeram-infinity-datasource/ or similar. Example: I have an n8n flow which provides a webhook to return the number of failed events in the last x hours as a simple number response. That flow calls the xyops API /api/app/search_jobs/v1, parses the response and returns a simple number for consumption through the Respond to webhook node. This is just an example, I'm actually looking into ways to potentially port (and centralize in xyops) "stuff" I run elsewhere. |
|
Added in xyOps v1.0.88 🚀 Check out the updated API docs to see how to use the feature: Basically, just add |
|
No need to apologize for the questions. I think the confusion here is mostly because you are using shell scripts, while the job messaging interface underneath xyOps is JSON-based. Let me try to separate the different concepts more clearly. First, this statement is not quite correct:
It is not just any JSON string. To send structured job data to xyOps, your script prints one complete xyOps Wire Protocol Message as a single line on STDOUT. For example: printf '%s\n' '{"xy":1,"data":{"message":"Hello","count":42}}'The entire line has to be valid JSON because that line is the message envelope which xyOps reads and interprets. The nested The So, from a shell job's point of view, there are two different kinds of output:
When I said the upcoming API response will include each sub-job's "raw output," I meant the captured text log for that job. Plain text, XML, and unrecognized JSON can all appear there as text. Binary data should not be written directly to STDOUT. For binary content, or any content which should remain a file, write it to disk and return it as a job output file instead. The response keeps raw There is one other detail which may simplify your shell workflow quite a bit. Parameters supplied to the outer workflow, such as user fields populated through a Magic Link, are passed to every sub-job. A full plugin can read them from the job JSON on STDIN under For example, if your workflow has a user field whose ID is #!/bin/sh
printf 'The workflow test parameter is: %s\n' "$workflow_test"If the field ID were If you want to return that value as structured data, use a JSON-aware encoder so quotes and other special characters are escaped correctly. For example, if jq is available on your servers, you can do this: jq -cn --arg test "$workflow_test" '{xy:1,data:{test:$test}}'The environment variable reference is here, and it also lists the other values available to jobs, including incoming data, shared workflow data, server data, event parameters, secrets, and job metadata: For shell-based workflows, a useful mental model is:
And yes, xyOps workflows can be a good fit for data ingestion and ETL orchestration. The individual steps can remain ordinary shell, Python, Node.js, or other tools. xyOps provides the scheduling, parameters, execution graph, retries, limits, logging, and movement of structured data and files between those steps. Sorry for the confusion, and hope this helps clear things up a bit. |

Ah, got it. Thank you for the grafana example. I understand the distinction now.
Yeah, so the current xyOps flow requires two requests:
stream_joband consume the SSE stream until the final job output arrives.While
stream_jobdoes contain the final output data, it still requires the caller to understand SSE and manage that second, long-lived request. That does not help clients such as the Grafana Infinity data source, which expect a single conventional HTTP request followed by a regular response.What you are describing is effectively a synchronous job HTTP API endpoint: launch a…