Install
$ agentstack add skill-devengagelab-engagelab-skills-engagelab-otp-verify ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo 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 Used
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ● Environment & secrets Used
- ✓ 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.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
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 →About
EngageLab OTP — Verify
Verifies a code the user entered against the one EngageLab generated and sent. Only applies to platform-generated mode (when you used send() not sendCustom()).
When to use this skill
You called send() → use this skill to verify
You called sendCustom() → do NOT use; verify against your own DB
Quick decision
The user typed a code. You have the message_id you stored from the send step. Call verify, branch on verified.
Endpoint
POST https://otp.api.engagelab.cc/v1/verifications
{
"message_id": "",
"verify_code": ""
}
Node.js
const { OTPClient, EngagelabError } = require('engagelab-otp');
const otp = new OTPClient(process.env.ENGAGELAB_DEV_KEY, process.env.ENGAGELAB_DEV_SECRET);
try {
const { verified } = await otp.verify(storedMessageId, userTypedCode);
if (verified) {
// ✓ Authentication passed — invalidate the message_id immediately
await session.markAuthenticated();
await deleteFromStore(storedMessageId);
} else {
// ✗ Wrong code — let the user try again (track attempts!)
await incrementFailedAttempts();
}
} catch (err) {
if (err instanceof EngagelabError && err.code === 3003) {
// Code has expired or was already used → ask user to request a new OTP
await sendNewOtp();
} else {
throw err;
}
}
Python
from engagelab_otp import OTPClient, EngagelabError
otp = OTPClient(os.environ["ENGAGELAB_DEV_KEY"], os.environ["ENGAGELAB_DEV_SECRET"])
try:
check = otp.verify(stored_message_id, user_typed_code)
if check["verified"]:
# ✓ Passed
mark_authenticated()
else:
# ✗ Wrong code
increment_failed_attempts()
except EngagelabError as e:
if e.code == 3003:
send_new_otp() # Expired or already verified
else:
raise
REST
curl -X POST https://otp.api.engagelab.cc/v1/verifications \
-H "Authorization: Basic $(echo -n $KEY:$SECRET | base64)" \
-H "Content-Type: application/json" \
-d '{
"message_id": "1725407449772531712",
"verify_code": "123456"
}'
Response
{
"message_id": "1725407449772531712",
"verify_code": "123456",
"verified": true
}
verified is the only field you should branch on.
Critical rules
1. ALWAYS verify server-side. Never trust a client claim of "verified".
2. Invalidate message_id after a successful verify.
Otherwise replay attacks succeed.
3. Limit retry attempts (typically 5).
Otherwise brute force succeeds.
4. Don't tell the user whether the code or the message_id is wrong.
Return a generic "verification failed" to attacker-facing surfaces.
5. After 3003 (expired), require user to request a new OTP — do not loop.
Sandbox
Sandbox mode (dev_key=sandbox_demo):
| Sandbox messageid source | verifycode | Result | |---------------------------|-------------|--------| | Any messageid from sandbox sends to +10000000005 | 123456 | verified: true | | Any messageid from sandbox sends to +10000000005 | anything else | verified: false | | Any sandbox messageid used twice | any | code 3003 (already used) | | Sandbox messageid older than 5 min | any | code 3003 (expired) |
When NOT to use this skill
- You called
sendCustom()(you generated the code) → check against your own DB - You want to know IF a message was delivered → use
engagelab-otp-webhookskill - You want to send a new code → use
engagelab-otp-sendskill
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: DevEngageLab
- Source: DevEngageLab/engagelab-skills
- License: MIT-0
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.