Skip to content
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions components/attestify/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Attestify

Attestify issues tamper-evident, cryptographically-verifiable certificates. Each certificate gets a
permanent public verify page anyone can check — Ed25519-signed, so it can't be forged after issuance.
Free, no signup required.

Learn more: https://attestify.novadyne.ai
18 changes: 18 additions & 0 deletions components/attestify/actions/issue-certificate/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Issue Certificate

Issue a tamper-evident, cryptographically-verifiable certificate with Attestify. Each certificate
gets a permanent public verify page anyone can check — Ed25519-signed, so it can't be forged after
issuance. Free, no signup.

**Props**
- **Organization / Issuer** (required) — shown on the certificate and the public verify page.
- **Course / Credential** (required) — what the certificate is for.
- **Recipient Name** (required) — name on the certificate.
- **Recipient Email** (optional) — echoed back so you can join it to the verify URL for a mail-merge
or LMS step. Never stored in the signed record and never shown on the public verify page.
- **Completion Date** (optional, `YYYY-MM-DD`) — defaults to today.

**Returns** the issued certificate: `cert_id`, `verify_url` (the permanent public page),
`signed_record_url` (the Ed25519-signed JSON), `cert_image_url`, plus the echoed fields.

Docs: https://attestify.novadyne.ai
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import app from "../../attestify.app.mjs";

export default {
key: "attestify-issue-certificate",
name: "Issue Certificate",
description:
"Issue a tamper-evident, cryptographically-verifiable certificate. Each certificate gets a permanent public verify page anyone can check — Ed25519-signed, so it can't be forged after issuance. Free, no signup. [See the docs](https://attestify.novadyne.ai).",
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
version: "0.0.1",
type: "action",
props: {
Comment thread
coderabbitai[bot] marked this conversation as resolved.
app,
issuer: { propDefinition: [app, "issuer"] },
course: { propDefinition: [app, "course"] },
recipientName: { propDefinition: [app, "recipientName"] },
recipientEmail: { propDefinition: [app, "recipientEmail"] },
completionDate: { propDefinition: [app, "completionDate"] },
},
async run({ $ }) {
if (this.completionDate && !/^\d{4}-\d{2}-\d{2}$/.test(this.completionDate)) {
throw new Error(
`Completion Date must be in YYYY-MM-DD format (got "${this.completionDate}")`,
);
}

const recipient = { name: this.recipientName };
if (this.recipientEmail) recipient.email = this.recipientEmail;

const body = {
issuer: this.issuer,
course: this.course,
recipients: [recipient],
};
if (this.completionDate) body.date = this.completionDate;

const resp = await this.app.issueCertificate($, body);

if (!resp || resp.ok !== true) {
const err = resp?.error ?? "unknown";
const detail = resp?.detail ? ` — ${resp.detail}` : "";
throw new Error(`Attestify error: ${err}${detail}`);
}

const cert = resp.certs && resp.certs[0];
if (!cert) {
throw new Error(
"Attestify returned no certificate (the recipient may have been empty, or the request was treated as an automated crawler).",
);
}

$.export(
"$summary",
`Issued certificate ${cert.cert_id} for ${cert.recipient_name} — verify at ${cert.verify_url}`,
);

return {
cert_id: cert.cert_id,
recipient_name: cert.recipient_name,
recipient_email: cert.recipient_email ?? (this.recipientEmail || undefined),
course: cert.course,
issuer: cert.issuer,
completion_date: this.completionDate || undefined,
verify_url: cert.verify_url,
cert_image_url: cert.cert_url,
signed_record_url: cert.json_url,
};
},
};
64 changes: 64 additions & 0 deletions components/attestify/attestify.app.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import { axios } from "@pipedream/platform";

// Attestify needs no credentials (free, no signup), so this app declares no auth.
// It exists as the branded namespace + shared HTTP method/prop definitions for the actions.
export default {
type: "app",
app: "attestify",
propDefinitions: {
issuer: {
type: "string",
label: "Organization / Issuer",
description:
"The organization issuing the certificate (shown on the certificate and the public verify page). Example: `Acme Real Estate Academy`.",
},
course: {
type: "string",
label: "Course / Credential",
description:
"The course or credential being certified. Example: `4-Hour Continuing Education — Ethics`.",
},
recipientName: {
type: "string",
label: "Recipient Name",
description: "Name of the certificate recipient.",
},
recipientEmail: {
type: "string",
label: "Recipient Email",
description:
"Optional. Echoed back so you can join it to the verify URL for your mail-merge or LMS. It is **never** stored in the signed record and **never** shown on the public verify page — recipient PII stays on your side.",
optional: true,
},
completionDate: {
type: "string",
label: "Completion Date",
description:
"Optional. The date the credential was earned, shown on the certificate (format `YYYY-MM-DD`). Leave empty to stamp today.",
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
optional: true,
},
},
methods: {
_baseUrl() {
return "https://attestify.novadyne.ai";
},
async _request($, { path, headers, ...opts } = {}) {
return axios($, {
...opts,
url: `${this._baseUrl()}${path}`,
headers: {
...headers,
// identify Pipedream-sourced issuances in telemetry (= the demand signal)
"User-Agent": "pipedream-attestify/0.0.1",
},
});
},
async issueCertificate($, data) {
return this._request($, {
method: "POST",
path: "/cert/issue",
data,
});
},
},
};
21 changes: 21 additions & 0 deletions components/attestify/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"name": "@pipedream/attestify",
"version": "0.0.1",
"description": "Pipedream Attestify Components — issue tamper-evident, cryptographically-verifiable certificates with a permanent public verify page.",
"main": "attestify.app.mjs",
"type": "module",
"keywords": [
"pipedream",
"attestify",
"certificates",
"verifiable-credentials"
],
"homepage": "https://pipedream.com/apps/attestify",
"author": "Novadyne",
"publishConfig": {
"access": "public"
},
"dependencies": {
"@pipedream/platform": "^3.0.3"
}
}