دمج التوقيع الإلكتروني في مكدسك التقني
أرسلوا المغلفات، تتبعوا التوقيعات، استقبلوا webhooks. API REST بسيطة، OpenAPI 3.0، أمثلة curl/Node/Python — كل ما تحتاجونه لربط Certyneo بنظام HRIS أو CRM أو برنامج عملك في بضع ساعات.
البدء السريع
ثلاث خطوات: أنشئ مفتاح API من الإعدادات، رمز ملف PDF الخاص بك بـ base64، أرسل. تحتوي الإجابة على `signUrl` التي يمكنكم مشاركتها مباشرة مع المستقبِل.
# 1. Upload the PDF (multipart) and capture the returned document id.
DOC_ID=$(curl -s -X POST https://certyneo.com/api/v1/documents \
-H "Authorization: Bearer sk_live_xxxxxxxxxxxxxxxxxxxxxxxx" \
-F "file=@contrat.pdf" | jq -r .id)
# 2. Create a DRAFT envelope referencing the uploaded document.
ENV_ID=$(curl -s -X POST https://certyneo.com/api/v1/envelopes \
-H "Authorization: Bearer sk_live_xxxxxxxxxxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d "{
\"subject\": \"Contrat de prestation\",
\"documentIds\": [\"$DOC_ID\"],
\"recipients\": [
{ \"email\": \"client@example.com\", \"name\": \"Marie Dubois\", \"role\": \"SIGNER\" }
]
}" | jq -r .id)
# 3. Dispatch the envelope — this sends the invitation email/SMS.
curl -X POST https://certyneo.com/api/v1/envelopes/$ENV_ID/send \
-H "Authorization: Bearer sk_live_xxxxxxxxxxxxxxxxxxxxxxxx"// npm install @certyneo/sdk (or call fetch directly)
const auth = { Authorization: `Bearer ${process.env.CERTYNEO_API_KEY}` };
// 1. Upload the PDF (multipart).
const fd = new FormData();
fd.append("file", new Blob([pdfBuffer], { type: "application/pdf" }), "contrat.pdf");
const doc = await fetch("https://certyneo.com/api/v1/documents", {
method: "POST", headers: auth, body: fd,
}).then((r) => r.json());
// 2. Create the DRAFT envelope.
const envelope = await fetch("https://certyneo.com/api/v1/envelopes", {
method: "POST",
headers: { ...auth, "Content-Type": "application/json" },
body: JSON.stringify({
subject: "Contrat de prestation",
documentIds: [doc.id],
recipients: [
{ email: "client@example.com", name: "Marie Dubois", role: "SIGNER" },
],
}),
}).then((r) => r.json());
// 3. Dispatch — this triggers the invitation channel for every recipient.
await fetch(`https://certyneo.com/api/v1/envelopes/${envelope.id}/send`, {
method: "POST", headers: auth,
});
console.log(envelope.id);import os, requests
auth = {"Authorization": f"Bearer {os.environ['CERTYNEO_API_KEY']}"}
# 1. Upload the PDF (multipart).
with open("contrat.pdf", "rb") as f:
doc = requests.post(
"https://certyneo.com/api/v1/documents",
headers=auth,
files={"file": ("contrat.pdf", f, "application/pdf")},
).json()
# 2. Create the DRAFT envelope.
envelope = requests.post(
"https://certyneo.com/api/v1/envelopes",
headers={**auth, "Content-Type": "application/json"},
json={
"subject": "Contrat de prestation",
"documentIds": [doc["id"]],
"recipients": [
{"email": "client@example.com", "name": "Marie Dubois", "role": "SIGNER"},
],
},
).json()
# 3. Dispatch — this triggers the invitation channel for every recipient.
requests.post(
f"https://certyneo.com/api/v1/envelopes/{envelope['id']}/send",
headers=auth,
)
print(envelope["id"])جرب API من أدواتك
يتم إنشاء مجموعة Postman وبطاقة RapidAPI من مواصفات OpenAPI التي توثق هذه الصفحة أيضاً. وبالتالي تبقى الثلاثة متوافقة مع واجهة برمجة التطبيقات الفعلية، نقطة نهاية تلو الأخرى، بدلاً من الاختلاف عند أول إضافة.
مجموعة Postman
22 طلباً مرتبة حسب المجال — الأغلفة والمستندات والقوالب والأختام والخطافات — مع مثال على النص والاستجابة لكل منها. الصق مفتاحك في متغير apiKey بالمجموعة، ثم قم بتشغيل GET /health: لا تطلب أي مصادقة وتؤكد أن إعدادك صحيح قبل أول استدعاء مصرح به.
المغلفات
الإنشاء والإرسال وتتبع الحالة والإلغاء. يمكن أن يحتوي المغلف على عدة مستندات وعدة موقعين (متوازي أو متسلسل).
ويب بوكس
جميع أحداث المغلف والمستقبل (`envelope.sent`, `recipient.signed`, `envelope.completed`...) يتم تسليمها إلى عنوان URL من اختيارك — القائمة الكاملة على /developers/webhooks. HMAC SHA-256 على كل حمولة للتحقق من الأصل.
المصادقة البسيطة
Bearer token. مفتاح واحد لكل بيئة (test / prod). يمكن إلغاؤه فوراً. الحد هو 100 طلب/دقيقة/مفتاح، مع burst يصل إلى 200، و 429 نظيف مع رأس Retry-After.
المسارات المتاحة
12 مساراً يغطي الدورة الكاملة: المغلفات والمستندات والـ webhooks ومفاتيح API. تقبل جميع المسارات Bearer token وتعيد JSON.
| Method | Path | Description |
|---|---|---|
| GET | /api/v1/account/me | Identity of the authenticated caller (id, email, plan) — scope-less credential probe |
| POST | /api/v1/documents | Upload a PDF (multipart) — returns document id |
| GET | /api/v1/documents | List documents |
| GET | /api/v1/documents/{id} | Fetch document metadata |
| DELETE | /api/v1/documents/{id} | Delete document |
| GET | /api/v1/envelopes | List envelopes (filter with ?status= and ?limit=) |
| POST | /api/v1/envelopes | Create envelope (status: DRAFT) — from a templateId, or from documentIds with an optional fields array |
| GET | /api/v1/envelopes/{id} | Fetch envelope state |
| PATCH | /api/v1/envelopes/{id} | Update DRAFT envelope |
| DELETE | /api/v1/envelopes/{id} | Void / delete DRAFT envelope |
| POST | /api/v1/envelopes/{id}/send | Dispatch DRAFT — sends invitations |
| GET | /api/v1/envelopes/{id}/audit-trail | Download eIDAS audit-trail PDF |
| GET | /api/v1/envelopes/{id}/signed-document | Download signed PDF (once COMPLETED) |
| GET | /api/v1/envelopes/bulk | List your bulk-send jobs |
| POST | /api/v1/envelopes/bulk | Bulk send: create N envelopes from one template + a CSV (Standard/Business only) |
| GET | /api/v1/envelopes/bulk/{id} | Bulk-send job progress: counts, per-row failures, created envelopes |
| GET | /api/v1/templates | List reusable envelope templates |
| POST | /api/v1/templates | Create a template (documents, roles, positioned fields) |
| POST | /api/v1/sepa-mandates | Generate a SEPA mandate PDF and its DRAFT envelope, fields already placed |
| POST | /api/v1/payroll-adapters/normalize | Normalise a payroll CSV (Silae, Sage Paie, PayFit, Lucca) into the bulk-send shape |
| POST | /api/v1/ag-copropriete | Create a condominium general-meeting envelope (resolutions + ownership shares) |
| POST | /api/v1/ag-copropriete/{envelopeId}/votes | Record a co-owner's votes on the meeting resolutions |
| POST | /api/v1/ag-copropriete/{envelopeId}/tally | Tally the meeting - per-resolution result weighted by ownership shares |
| GET | /api/v1/videos/{videoId} | Download a stored identity video - GDPR art. 15 access path |
| GET | /api/v1/webhooks | List webhooks |
| POST | /api/v1/webhooks | Register webhook — returns the signing secret once |
| GET | /api/v1/webhooks/{id} | Fetch webhook subscription |
| PATCH | /api/v1/webhooks/{id} | Update url / events / active state |
| DELETE | /api/v1/webhooks/{id} | Unregister |
| POST | /api/v1/seals | Apply a qualified electronic seal to a document |
| GET | /api/v1/seals/{id} | Fetch seal status |
| GET | /api/v1/seals/{id}/certificate | Download the seal certificate |
| GET | /api/v1/keys | List API keys |
| POST | /api/v1/keys | Create API key — the secret is shown once |
| PATCH | /api/v1/keys/{id} | Rename / revoke key |
| DELETE | /api/v1/keys/{id} | Delete key |
| GET | /api/v1/billing/usage | Current period usage and projected cost |
| GET | /api/v1/status | Service status |
| GET | /api/v1/openapi | Machine-readable OpenAPI specification |
قواميس الغلاف
يسجل النموذج مرة واحدة وإلى الأبد ملف PDF والأدوار وموضع حقول التوقيع، ثم يُعاد استخدامه في كل إرسال: مرّر معرّفه في templateId بدلاً من documentIds، وسيتم نسخ الحقول المحددة الموضع على الظرف المنشأ.
- • يتم إنشاء النماذج من لوحة التحكم (النماذج → نموذج جديد)، حيث تقوم بإيداع المستند ثم وضع الحقول بالماوس — هذا هو المسار الأبسط. تسمح API أيضاً بإنشاؤها عبر POST /api/v1/templates، بتوفير المستندات والأدوار والحقول؛ كن حذراً، يتم وضع الحقول هناك في إحداثيات مطلقة (صفحة، x، y، عرض، ارتفاع)، لذا يجب أن تعرف تخطيط ملف PDF.
- • على حساب لم يسجل بعد أي نموذج، يعيد GET /api/v1/templates قائمة فارغة. هذا هو السلوك الطبيعي، وليس خلل في المصادقة.
- • تحتوي القائمة فقط على النماذج التي يمتلكها المستخدم صاحب مفتاح API. النموذج الذي أنشأه زميل لا يظهر فيها، حتى داخل مساحة عمل مشتركة: أنشئ المفتاح من الحساب الذي يمتلك النموذج.
- • يستبعد templateId و documentIds بعضهما البعض: أرسل أحدهما أو الآخر، وليس كليهما ولا أيًا منهما.
- • قدم عددًا من متلقي SIGNER على الأقل بقدر عدد أدوار الموقعين التي يحتويها النموذج، وإلا يتم رفض الإنشاء برمز 400. يشير الحقل signerCount الذي تعيده القائمة إلى العدد المتوقع.
- • يتم وراثة مستوى التوقيع للنموذج من قبل المغلف، إلا إذا مررت الطلب صراحةً signatureLevel.
# 1. Discover the templates saved on this account.
curl -s https://certyneo.com/api/v1/templates \
-H "Authorization: Bearer sk_live_xxxxxxxxxxxxxxxxxxxxxxxx"
# {
# "data": [
# { "id": "cmdq7f4k80001s6y2h1xa9pl3", "name": "Contrat de prestation",
# "signerCount": 2, "documentCount": 1, "signatureLevel": "SIMPLE" }
# ],
# "pagination": { "page": 1, "pageSize": 20, "total": 1, "pages": 1 }
# }
#
# An empty "data" array means no template exists on this account yet —
# create one from the dashboard, it is not an authentication problem.
# 2. Create the envelope FROM the template: no documentIds and no field
# coordinates, both are carried by the template. Pass one SIGNER
# recipient per signer role, in the template's role order.
curl -X POST https://certyneo.com/api/v1/envelopes \
-H "Authorization: Bearer sk_live_xxxxxxxxxxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{
"subject": "Contrat de prestation",
"templateId": "cmdq7f4k80001s6y2h1xa9pl3",
"recipients": [
{ "email": "client@example.com", "name": "Marie Dubois", "role": "SIGNER" },
{ "email": "legal@example.com", "name": "Paul Martin", "role": "SIGNER" }
]
}'
# 3. The envelope is DRAFT at this point — POST /envelopes/{id}/send
# dispatches it, exactly as in the quick-start above.من خلال Power Automate أو Zapier
إجراء "إنشاء مغلف" في موصلاتنا بدون كود يغطي فقط المسار حسب النموذج: حقل النموذج فيه إجباري. بالنسبة لمستند مخصص يتغير في كل تنفيذ، استخدم إجراء "تحميل مستند" ثم إجراء HTTP خام إلى POST /api/v1/envelopes مع تمرير documentIds المُعاد.
إرسال المستند: شكلان مقبولان
يقبل POST /api/v1/documents الملف بطريقتين، حسب الاختيار. تنطبق نفس الضوابط في كلا الحالتين: الأنواع المسموحة، سقف 50 ميجابايت، التحقق من التوقيع الثنائي وتحليل مكافحة الفيروسات.
- • في multipart/form-data، مع جزء يُسمى file. هذا هو الشكل الكلاسيكي، وهو ما يفعله curl -F وأغلب المكتبات.
- • في الجسم الخام: بايتات الملف تشكل جسم الطلب، و Content-Type الرأس يعطيه النوع (application/pdf على سبيل المثال). مفيد من أداة تنقل المحتوى كما هو، دون تغليف الطلب — هذا ما يفعله موصل Power Automate.
- • في النص الخام، اسم الملف لا يملك مكاناً في النص: حدده عبر رأس الطلب X-File-Name أو المعامل ?fileName=. بدونه، يُسمى المستند بناءً على نوعه.
- • نوع غير مدعوم يرد بـ 415 مع تسمية الشكلين المقبولين، ونص مُعلن كـ multipart لكن غير قابل للقراءة يرد بـ 400. لا أحدهما خطأ من جانب الخادم.
# a. multipart/form-data — the classic shape.
curl -X POST https://certyneo.com/api/v1/documents \
-H "Authorization: Bearer sk_live_xxxxxxxxxxxxxxxxxxxxxxxx" \
-F "file=@contrat.pdf;type=application/pdf"
# b. raw body — the file bytes ARE the body, typed by Content-Type.
# The filename has nowhere to live in the body, so pass it as a header
# (or ?fileName=). Without it the document is named after its type.
curl -X POST https://certyneo.com/api/v1/documents \
-H "Authorization: Bearer sk_live_xxxxxxxxxxxxxxxxxxxxxxxx" \
-H "Content-Type: application/pdf" \
-H "X-File-Name: contrat.pdf" \
--data-binary "@contrat.pdf"
# Both return the same 201 with the document id to pass as documentIds.إضافة حقول التوقيع
بدون نموذج، المغلف الذي تم إنشاؤه من documentIds لا يحتوي على أي حقول محددة مسبقًا: يتلقى الموقع المستند بدون موقع للتوقيع عليه. مصفوفة fields، المرسلة في نفس استدعاء الإنشاء، تضع كل حقل بدقة — هذا هو المكافئ من جانب API لما يسجله النموذج مرة واحدة إلى الأبد.
- • الإحداثيات بالنقاط PDF، الأصل في الزاوية العلوية اليسرى للصفحة والمحور Y لأسفل (صفحة A4 بقياس 595 × 842 نقطة). x و y يشيران إلى الزاوية العلوية اليسرى للحقل، width و height حجمه.
- • pageNumber يبدأ من 1، documentIndex يبدأ من 0. رقم صفحة يتجاوز المستند لا يتم رفضه عند الإنشاء: يتم تجاهل الحقل وقت التوقيع ولا يظهر في أي مكان — هذا أول شيء يجب التحقق منه عندما يفقد حقل في الاستدعاء.
- • يجب أن يطابق recipientEmail أحد متلقي نفس الاستدعاء، بدون تمييز الأحرف الكبيرة والصغيرة. وإلا فشل الإنشاء بسرد جميع الصفوف الخاطئة، مما يتجنب تصحيحها واحدة تلو الأخرى.
- • يستبعد fields و templateId بعضهما البعض: النموذج يحمل بالفعل تخطيطه الخاص. لذلك لا تُستخدم مصفوفة fields إلا مع documentIds.
- • الأنواع المقبولة: SIGNATURE و INITIALS و DATE_SIGNED و TEXT و CHECKBOX و RADIO_GROUP. required يساوي true بشكل افتراضي؛ placeholder و dateFormat اختيارية، و options يتم الاحتفاظ به فقط لـ RADIO_GROUP.
- • يقبل المغلف حد أقصى من 100 حقل و 20 مستند و 50 متلقي — قد تكون حدود خطتك أقل.
# Ad-hoc envelope WITH pre-placed fields — no template involved.
# Coordinates are PDF points, origin TOP-LEFT of the page, +Y downwards
# (A4 = 595 x 842 pt). x / y are the field box's top-left corner.
#
# The last field is positioned by ANCHOR instead of by eye: the server
# locates "Signature du client" in the PDF and computes the spot. x / y
# stay required there — they are the fallback if the text is not found.
curl -X POST https://certyneo.com/api/v1/envelopes \
-H "Authorization: Bearer sk_live_xxxxxxxxxxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{
"subject": "Contrat de prestation",
"documentIds": ["cmdq7f4k80001s6y2h1xa9pl3"],
"recipients": [
{ "email": "client@example.com", "name": "Marie Dubois", "role": "SIGNER" }
],
"fields": [
{ "recipientEmail": "client@example.com", "documentIndex": 0,
"pageNumber": 2, "fieldType": "SIGNATURE",
"x": 90, "y": 640, "width": 180, "height": 44 },
{ "recipientEmail": "client@example.com", "documentIndex": 0,
"pageNumber": 2, "fieldType": "DATE_SIGNED",
"x": 320, "y": 640, "width": 140, "height": 30,
"dateFormat": "DD/MM/YYYY" },
{ "recipientEmail": "client@example.com", "documentIndex": 0,
"pageNumber": 2, "fieldType": "TEXT",
"x": 90, "y": 700, "width": 200, "height": 30,
"placeholder": "Fonction", "required": false },
{ "recipientEmail": "client@example.com", "documentIndex": 0,
"pageNumber": 2, "fieldType": "SIGNATURE",
"anchorText": "Signature du client", "anchorPlacement": "below",
"anchorIndex": 0,
"x": 90, "y": 640, "width": 180, "height": 44 }
]
}'
# The envelope is DRAFT at this point — POST /envelopes/{id}/send
# dispatches it, exactly as in the quick-start above.الأوتار النصية: وضع حقل دون معرفة الإحداثيات
بدلاً من الإحداثيات، يمكن لحقل أن يستشهد بنص مطبوع في المستند: anchorText يحدده في PDF والخادم يحسب الموضع عند الإنشاء. هذا هو الوضع المفضل عندما يتم إعادة إنشاء المستند في كل إرسال — دمج المراسلات، منشئ العقود — لأن التخطيط يتغير بينما تبقى عبارة "توقيع العميل". يشير anchorPlacement إلى أي جانب من النص يقع الحقل عليه (right بشكل افتراضي، وإلا below أو above أو left) و anchorIndex يختار حدوث النص عندما يظهر عدة مرات.
يظل x و y إجباريين حتى مع ربط: يخدمان كنسخة احتياطية. لا يفشل الإنشاء إذا لم يتم العثور على ربط — يحتفظ الحقل بالموضع الحرفي الذي قدمته، بدون خطأ أو تحذير في الاستجابة. لذا قدم نسخة احتياطية معقولة بدلاً من 0,0، وتحقق من العرض عند الإرسال الأول.
التحقق من الهوية
كل طلب يحتوي على مفتاح API في رأس الطلب Authorization. يتم إنشاؤها من الإعدادات → مفاتيح API ولا تظهر إلا مرة واحدة.
GET /api/v1/account/me HTTP/1.1Host: certyneo.com
Authorization: Bearer sk_live_xxxxxxxxxxxxxxxxxxxxxxxx
# 200 OK
{ "data": { "id": "usr_…", "email": "you@example.com", "plan": "BUSINESS", "environment": "live" } }- • تنسيق: sk_live_… في الإنتاج، sk_test_… للبيئة التجريبية. رأس: Authorization: Bearer <المفتاح>.
- • النطاقات: التوقيعات، الوثائق، الأحداث الفورية، الشبكات - قراءة (:read) أو كتابة (:write). الكتابة تتطلب القراءة؛ النطاق * يمنح جميع الحقوق.
- • المفاتيح sk_test_ تخلق الموارد في البيئة التجريبية، وهي محصورة ولا تخضع للحد الأقصى أو التسعير.
- • الخطأ: 401 مفتاح غير صالح، 403 نطاق غير كافٍ، 429 حد سرعة متجاوز، 402 الحد الأقصى الشهري تجاوز.
صيغ الرد
نقطة مهمة يجب معرفتها قبل كتابة العميل الخاص بك: المجموعات مغلفة داخل كائن data، بينما الموارد الفردية تُرجع بشكل مباشر. لذلك فإن قراءة response.data.data على مورد فردي تُرجع undefined.
// GET /api/v1/envelopes
// Collections are WRAPPED in a "data" array.
{
"data": [
{ "id": "env_abc123", "subject": "Contrat", "status": "SENT" }
]
}// GET /api/v1/envelopes/{id}
// Single resources are returned FLAT — no "data" envelope.
{
"id": "env_abc123",
"subject": "Contrat",
"status": "COMPLETED",
"recipients": [ /* … */ ]
}حدود معدل الطلب
تضمن الحدود جودة خدمة مستقرة لجميع العملاء. إذا احتجتم إلى المزيد، يرجى التواصل معنا.
- • 100 طلب في الدقيقة لكل مفتاح API
- • Burst مقبول يصل إلى 200 طلب في أقل من 10 ثوان
- • رد 429 مع رأس Retry-After يشير إلى التأخير بالثواني