We build custom applications 5x faster and cheaper 🚀
Book a Free Consultation
Stuck on an error? Book a 30-minute call with an engineer and get a direct fix + next steps. No pressure, no commitment.
OpenClaw can invoke a Summarize skill the same way it invokes any external service: you register the skill through ClawHub, provide a secure HTTPS endpoint (or use ClawHub-hosted runtime if available), configure authentication (OAuth or API key) and environment variables in ClawHub, and implement a simple, well-documented REST API that the agent calls. Keep all durable state and heavy work outside the agent runtime (databases, queues, background workers). Secure calls with scoped credentials, validate incoming webhook/signature headers, and debug by checking runtime logs, API responses, credentials/scopes, and that the skill was invoked. Below are concrete, vendor-neutral steps, examples, and code you can use now.
<b>//</b> server.js - minimal Summarize skill
const express = require('express');
const fetch = require('node-fetch');
const crypto = require('crypto');
const app = express();
app.use(express.json());
const SUMMARIZE_API_URL = process.env.SUMMARIZE_API_URL; <b>//</b> e.g. https://api.summarize.example/v1/summarize
const SUMMARIZE_API_KEY = process.env.SUMMARIZE_API_KEY; <b>//</b> store in ClawHub secret store
const INCOMING_SECRET = process.env.INCOMING_SECRET; <b>//</b> optional secret for validating ClawHub webhooks
<b>//</b> helper to call external Summarize service
async function callSummarizeService(text, opts = {}) {
const payload = {
text,
max_length: opts.max_length || 300,
style: opts.style || 'concise'
};
const res = await fetch(SUMMARIZE_API_URL, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${SUMMARIZE_API_KEY}`
},
body: JSON.stringify(payload)
});
if (!res.ok) {
const body = await res.text();
const err = new Error(`Summarize API error ${res.status}: ${body}`);
err.status = res.status;
throw err;
}
return res.json();
}
app.post('/summarize', async (req, res) => {
try {
const sig = req.headers['x-incoming-signature'];
if (INCOMING_SECRET) {
<b>//</b> validate incoming webhook signature if ClawHub/agent provides one
const hmac = crypto.createHmac('sha256', INCOMING_SECRET);
hmac.update(JSON.stringify(req.body));
const expected = `sha256=${hmac.digest('hex')}`;
if (!sig || sig !== expected) return res.status(401).json({ error: 'invalid signature' });
}
const { text, max_length, style } = req.body;
if (!text) return res.status(400).json({ error: 'text is required' });
const result = await callSummarizeService(text, { max_length, style });
<b>//</b> normalize response for OpenClaw/agent consumer
return res.json({ summary: result.summary || result.text, metadata: { original_length: text.length } });
} catch (err) {
console.error('summarize error', err);
return res.status(err.status || 500).json({ error: err.message || 'internal error' });
}
});
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`Summarize skill listening on ${PORT}`);
});
<b>//</b> raw test call to your skill
curl -X POST 'https://your-skill.example/summarize' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer SKILL_INVOKER_TOKEN' \
-d '{"text":"Long article content goes here","max_length":200}'
<b>//</b> validateSignature(rawBody, headerSig, secret)
const crypto = require('crypto');
function validateSignature(rawBody, headerSig, secret) {
const hmac = crypto.createHmac('sha256', secret);
hmac.update(rawBody);
const expected = `sha256=${hmac.digest('hex')}`;
<b>//</b> constant-time compare
return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(headerSig));
}
Speak one‑on‑one with a senior engineer about your no‑code app, migration goals, and budget. In just half an hour you’ll leave with clear, actionable next steps—no strings attached.
1
Direct answer: A 401 means the Connector did not present a valid credential to the Summarize API at request time. Storing the key in the secrets store is necessary but not sufficient — the running connector must be granted access to that secret and must place the secret into the outgoing request in the exact header/format the API expects.
Common causes:
Verify secret value in runtime and test a request:
```js // Node.js: ensure process.env.SUMMARIZE_API_KEY is set in connector config fetch('https://api.example.com/summarize',{ method:'POST', headers:{ 'Authorization':`Bearer ${process.env.SUMMARIZE_API_KEY}` }, body: JSON.stringify({text:'...'}) }) ```Also test with curl to isolate OpenClaw:
```bash # // replace KEY with the runtime value curl -H "Authorization: Bearer KEY" https://api.example.com/summarize ```2
The pipeline fails with 400 Bad Request because the JSON you forward does not match the Summarize skill’s declared input schema: field names, nesting, or data types are different (missing required fields, strings vs arrays, wrong key names). The OpenClaw runtime performs strict schema validation before invoking a skill and rejects payloads that don’t conform.
Check the skill’s input schema in ClawHub, examine runtime logs for validation errors, and validate your payload locally against that schema.
// Example: POST payload must match the skill schema (e.g. "text" field is required)
fetch('', {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' },
body: JSON.stringify({ text: 'Document text to summarize' })
});
3
Direct answer: OpenClaw rejects Summarize webhook callbacks when the computed signature doesn’t match or the X-Signature header is missing — usually because the sender used the wrong secret, a proxy/middleware altered the raw request body or headers, the header was stripped, or timestamp/algorithm expectations differ.
4
Direct answer: Summaries get truncated and OpenClaw task workers time out when the model's context/token limit is exceeded or when the task configuration doesn't enforce a max_input_length, causing the runtime to send oversized requests or stall while retrying large payloads.
// Example: trim messages to model token budget (Node.js)
// uses a tokenizer like tiktoken; compute tokens then slice history
async function prepareInput(messages, maxTokens){
// trim from oldest until within budget
while(tokenCount(messages) > maxTokens) messages.shift();
return messages;
}
This prompt helps an AI assistant understand your setup and guide you through the fix step by step, without assuming technical knowledge.
From startups to enterprises and everything in between, see for yourself our incredible impact.
Need a dedicated strategic tech and growth partner? Discover what RapidDev can do for your business! Book a call with our team to schedule a free, no-obligation consultation. We’ll discuss your project and provide a custom quote at no cost.Â