الانتقال إلى المحتوى الرئيسي
Certyneo
API العام v1

دمّج التوقيع الإلكتروني في مكدس التكنولوجيا الخاص بك

أرسل المغاليف، تابع التوقيعات، استقبل webhooks. API REST بسيط، OpenAPI 3.0، أمثلة curl/Node/Python — كل ما تحتاجه لربط Certyneo بنظام إدارة الموارد البشرية أو CRM أو برنامجك خلال بضع ساعات.

البدء السريع

ثلاث خطوات: أنشئ مفتاح API من الإعدادات، قم بترميز ملف PDF الخاص بك بصيغة base64، أرسله. تحتوي الاستجابة على `signUrl` الذي يمكنك مشاركته مباشرة مع المستقبل.

cURLbash
# 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"
JavaScript / Nodets
// Plain fetch, no SDK to install.
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);
Pythonpython
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

25 طلباً مرتبة حسب المجال — الأغلفة والمستندات والقوالب والأختام والخطافات — مع مثال على النص والاستجابة لكل منها. الصق مفتاحك في متغير apiKey بالمجموعة، ثم قم بتشغيل GET /health: لا تطلب أي مصادقة وتؤكد أن إعدادك صحيح قبل أول استدعاء مصرح به.

بطاقة RapidAPI

نفس فهرس نقاط النهاية، قابل للاختبار مباشرة من المتصفح. منصة الاختبار تتوقع رأسين مختلفين: مفتاح RapidAPI الذي تخصصه لك المنصة، ومفتاح Certyneo الخاص بك في Authorization — الثاني هو الذي يصرح الاستدعاء فعلياً.

الأظرف

الإنشاء والإرسال والمراقبة والإلغاء. يمكن للظرف أن يحتوي على عدة مستندات وعدة موقعين (متوازي أو متسلسل).

شبكات الإنترنت

جميع أحداث المغلف والمستقبل (`envelope.sent`, `recipient.signed`, `envelope.completed`...) يتم تسليمها إلى عنوان URL من اختيارك — القائمة الكاملة على /developers/webhooks. HMAC SHA-256 على كل حمولة للتحقق من الأصل.

المصادقة البسيطة

Bearer token. مفتاح واحد لكل بيئة (الاختبار / الإنتاج). قابل للإلغاء فوراً. الحد 100 طلب/دقيقة/مفتاح، رفقة 200، 429 نظيفة مع رأس Retry-After.

نقاط النهاية المتاحة

المسارات 53 العامة: الحساب والمستندات والأغلفة والنماذج وخطافات الويب والأختام الإلكترونية ومفاتيح واجهة برمجة التطبيقات والفواتير. تقبل جميعها رمز Bearer وترجع JSON.

الطريقةالمسارالوصف
GET/api/v1/account/meهوية المتصل المصرح به (معرّف، بريد إلكتروني، خطة): اختبار المعرّفات بدون نطاق مطلوب
GET/api/v1/sandboxإحصاء البيانات المُنشأة بمفاتيح الاختبار (تُحذف تلقائيًا بعد 30 يومًا)
DELETE/api/v1/sandboxحذف جميع البيانات المُنشأة بمفاتيح الاختبار، دون المساس بالبيانات الحقيقية
POST/api/v1/documentsتحميل ملف PDF (متعدد الأجزاء): يُرجع معرّف المستند
GET/api/v1/documentsإدراج قائمة بالمستندات
GET/api/v1/documents/{id}قراءة بيانات وصفية للمستند
DELETE/api/v1/documents/{id}حذف مستند
GET/api/v1/envelopesإدراج قائمة بالأظرف (تصفية باستخدام ?status= و?limit=)
POST/api/v1/envelopesإنشاء ظرف (حالة DRAFT): من templateId أو من documentIds مع جدول fields اختياري
GET/api/v1/envelopes/{id}قراءة حالة الظرف
PATCH/api/v1/envelopes/{id}تعديل ظرف DRAFT
DELETE/api/v1/envelopes/{id}حذف ظرف DRAFT (409 بعد الإرسال: قم بإلغاؤه بدلاً من ذلك)
POST/api/v1/envelopes/{id}/sendإرسال ظرف DRAFT: يُرسل الدعوات
POST/api/v1/envelopes/{id}/voidإلغاء ظرف مُرسَل لم يتم توقيعه بالكامل بعد: يتم إخطار الموقّعين المعلقين
POST/api/v1/envelopes/{id}/remindإعادة إرسال تذكير عند الطلب للموقعين في انتظار مظروف مرسل (مستقبل محدد اختياري)
GET/api/v1/envelopes/{id}/audit-trailتنزيل ملف PDF لسجل إثبات eIDAS
POST/api/v1/envelopes/{id}/embed-urlيعيد عنوان URL للتوقيع لمستقبل، مصحوباً بترخيص التضمين للأصل المطلوب. يتم استدعاؤه عند النقر: الترخيص صالح لمدة ساعتين.
GET/api/v1/audit-anchors/{root}عام: بيانات وصفية لدفعة تدقيق مرساة، أو ملف إثبات .ots مع ?format=ots (بدون مصادقة)
POST/api/v1/audit-anchors/verifyعام: التحقق من إدخال تدقيق وإثباته مقابل جذر Merkle المُرسى (بدون مصادقة، لا يكشف أي شيء)
GET/api/v1/envelopes/{id}/signed-documentتنزيل ملف PDF الموقّع (بعد COMPLETED)
GET/api/v1/envelopes/bulkإدراج قائمة بعمليات الإرسال الجماعية الخاصة بك
POST/api/v1/envelopes/bulkالإرسال الجماعي: إنشاء N ظرف من نموذج وملف CSV (Standard/Business فقط)
GET/api/v1/envelopes/bulk/{id}تقدم الإرسال الجماعي: العدادات والأخطاء حسب الصف والأظرف المُنشأة
GET/api/v1/templatesإدراج نماذج الأظرف القابلة لإعادة الاستخدام
POST/api/v1/templatesإنشاء نموذج (المستندات والأدوار والحقول المُوضعة)
GET/api/v1/workspacesإدراج مساحات العمل التي يمكنك إنشاء ظرف فيها
POST/api/v1/sepa-mandatesتوليد ملف PDF لتفويض SEPA وظرفه DRAFT مع الحقول المُوضعة مسبقاً
POST/api/v1/payroll-adapters/normalizeتطبيع ملف CSV للرواتب (Silae وSage Paie وPayFit وLucca) إلى صيغة الإرسال الجماعي
POST/api/v1/ag-coproprieteإنشاء ظرف لجمعية عامة للملكية المشتركة (القرارات والحصص)
POST/api/v1/ag-copropriete/{envelopeId}/votesتسجيل أصوات مالك الملكية المشتركة على قرارات الجمعية
POST/api/v1/ag-copropriete/{envelopeId}/tallyفرز الجمعية: النتيجة حسب القرار مرجحة بالحصص
GET/api/v1/videos/{videoId}تحميل فيديو الهوية المحفوظة: حق الوصول بموجب RGPD (المادة 15)
GET/api/v1/webhooksإدراج webhooks
POST/api/v1/webhooksتسجيل webhook: إرجاع سر التوقيع مرة واحدة فقط
GET/api/v1/webhooks/{id}قراءة اشتراك webhook
PATCH/api/v1/webhooks/{id}تعديل عنوان URL أو الأحداث أو الحالة النشطة
DELETE/api/v1/webhooks/{id}إلغاء الاشتراك في webhook
POST/api/v1/sealsإضافة ختم إلكتروني موثوق على مستند
GET/api/v1/seals/{id}قراءة حالة الختم
GET/api/v1/seals/{id}/certificateتحميل شهادة الختم
GET/api/v1/payslips/workspacesكشوف الرواتب: المساحات التي يوزعها هذا المفتاح والمحتفظ بها أو المفوضة من قبل عميل (مكاتب المحاسبة ومديرو الرواتب)
GET/api/v1/payslips/employeesكشوف الرواتب: سجل الموظفين مع أولئك الذين رفضوا التسليم الإلكتروني (كشف رواتب ورقي مستحق)
PUT/api/v1/payslips/employeesكشوف الرواتب: إضافة أو تحديث الموظفين في السجل (لا تزيلهم أبداً)
POST/api/v1/payslipsتسليم كشف راتب (PDF) في خزينة الموظف: مختوم، محفوظ 50 سنة، إعادة محاولة بدون تكرار
GET/api/v1/payslipsإدراج كشوف الرواتب المسلمة، مع إثبات استشارتها
GET/api/v1/keysإدراج مفاتيح API
POST/api/v1/keysإنشاء مفتاح API: يتم عرض السر مرة واحدة فقط
PATCH/api/v1/keys/{id}إعادة تسمية أو إلغاء مفتاح
DELETE/api/v1/keys/{id}حذف مفتاح
GET/api/v1/billing/usageالاستهلاك للفترة الحالية والتكلفة المتوقعة
GET/api/v1/statusحالة الخدمة
GET/api/v1/healthفحص الصحة: قاعدة البيانات، قائمة الانتظار وتخزين الكائنات
GET/api/v1/openapiمواصفات OpenAPI قابلة للقراءة بواسطة الآلة

قواميس الغلاف

يسجل النموذج مرة واحدة وإلى الأبد ملف PDF والأدوار وموضع حقول التوقيع، ثم يُعاد استخدامه في كل إرسال: مرّر معرّفه في templateId بدلاً من documentIds، وسيتم نسخ الحقول المحددة الموضع على الظرف المنشأ.

  • • يتم إنشاء النماذج من لوحة التحكم (النماذج → نموذج جديد)، حيث تقوم بإيداع المستند ثم وضع الحقول بالماوس — هذا هو المسار الأبسط. تسمح API أيضاً بإنشاؤها عبر POST /api/v1/templates، بتوفير المستندات والأدوار والحقول؛ كن حذراً، يتم وضع الحقول هناك في إحداثيات مطلقة (صفحة، x، y، عرض، ارتفاع)، لذا يجب أن تعرف تخطيط ملف PDF.
  • • على حساب لم يسجل بعد أي نموذج، يعيد GET /api/v1/templates قائمة فارغة. هذا هو السلوك الطبيعي، وليس خلل في المصادقة.
  • • تحتوي القائمة فقط على النماذج التي يمتلكها المستخدم صاحب مفتاح API. النموذج الذي أنشأه زميل لا يظهر فيها، حتى داخل مساحة عمل مشتركة: أنشئ المفتاح من الحساب الذي يمتلك النموذج.
  • • يستبعد templateId و documentIds بعضهما البعض: أرسل أحدهما أو الآخر، وليس كليهما ولا أيًا منهما.
  • • قدم عددًا من متلقي SIGNER على الأقل بقدر عدد أدوار الموقعين التي يحتويها النموذج، وإلا يتم رفض الإنشاء برمز 400. يشير الحقل signerCount الذي تعيده القائمة إلى العدد المتوقع.
  • • يتم وراثة مستوى التوقيع للنموذج من قبل المغلف، إلا إذا مررت الطلب صراحةً signatureLevel.
cURLbash
# 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. لا أحدهما خطأ من جانب الخادم.
cURLbash
# 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 متلقي — قد تكون حدود خطتك أقل.
cURLbash
# 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، وتحقق من العرض عند الإرسال الأول.

التوقيع المدمج: وقّع دون مغادرة تطبيقك

يمكن لعميلك مراجعة وتوقيع مستنده من واجهتك الخاصة. تفتح مراسم التوقيع في نافذة فوق صفحتك، ويعود إليك تلقائياً عند الانتهاء. من جانب الخادم، لا جديد: تنشئ الظرف كالمعتاد بملء redirectUrl، ثم تسند إلى SDK عنوان التوقيع للمستلم.

  • • استدعِ Certyneo.sign() مباشرة في معالج النقر، وأرسل استدعاء الخادم عبر getUrl. انتظر قبل sign() يخرج من الإجراء: Safari و Firefox يحجبان النافذة حينئذٍ. يفتحها SDK على الفور ويحملها عند تحقق وعدك.
  • • redirectUrl هي قناة الإرجاع. نعيد توجيه الموقّع إليها مع إضافة certyneo_status (completed أو declined) و certyneo_envelope. حمّل نفس البرنامج النصي على هذه صفحة الإرجاع: هو الذي ينبّه نافذة البوابة التي بقيت مفتوحة في الخلفية.
  • • في الوضع المدمج، تتم إزالة استدعاءات الإجراء الخاصة بنا. لا يرى عميلك أبداً دعوة لإنشاء حساب Certyneo.
  • • إذا رفض المتصفح النافذة، ينتقل SDK تلقائياً إلى إعادة توجيه الصفحة الكاملة. المسار متطابق للموقّع وتستقبل صفحة الإرجاع الخاصة بك المعاملات نفسها تماماً.
  • • لا توجد عن قصد دالة استدعاء "المستخدم تخلى عن العملية": حالة النافذة غير قابلة للقراءة من صفحتك، وستُطلق مثل هذه الدالة بالخطأ. التوقيع الذي لا يصل يبقى في حالة الانتظار حتى webhook.
JavaScriptjavascript
// 1. Your server creates and sends the envelope, then hands the browser the
//    recipient's signing URL. Nothing new on the API side:
//      POST /api/v1/envelopes        { …, "redirectUrl": "https://your-app.com/quote/42/signed" }
//      POST /api/v1/envelopes/{id}/send   → recipients[].accessToken
//      signingUrl = "https://certyneo.com/sign/" + accessToken

// 2. On your quote page — load the SDK once:
//      <script src="https://certyneo.com/embed.js"></script>

document.querySelector("#sign").addEventListener("click", () => {
  Certyneo.sign({
    // Called AFTER the window is already open, so no popup blocker ever
    // sees a delay between the click and window.open.
    getUrl: () =>
      fetch("/api/quote/42/signature", { method: "POST" })
        .then((r) => r.json())
        .then((d) => d.signingUrl),

    onCompleted: (e) => showConfirmation(e.envelopeId),
    onDeclined: () => showDeclined(),
    onError: (e) => showError(e),
  });
});

// 3. On https://your-app.com/quote/42/signed — your return page — load the
//    same script and render a short confirmation. It relays the outcome to
//    the portal window behind it, then closes itself.

حدث المتصفح ليس إثباتاً للتوقيع

onCompleted هي إشارة عرض، تُصدرها متصفح، لذلك يمكن لأي شخص تزييفها من وحدة التحكم الخاصة به. لا تنقل الملف أبداً إلى حالة "موقّع" بناءً على ذلك: استخدمها لتحديث الشاشة، واعتمد على webhook المُوقّع envelope.completed الذي يستقبله خادمك للحالة الفعلية. هذا هو الفخ الكلاسيكي لتكاملات الدفع، ويتكرر بنفس الطريقة هنا.

التطوير على جهازك

مع مفتاح اختبار، redirectUrl يقبل http://localhost (و 127.0.0.1): تصعد كل المسار على جهازك قبل النشر. مفتاح حقيقي يرفضه، حتى لا توجه أي مظروف إنتاجي موقّعاً حقيقياً إلى جهاز ليس ملكه.

عرض الحفل في صفحتك

يعرض وضع الإطار التوقيع داخل واجهتك، بدون نافذة منفصلة. يتطلب خطوة إضافية واحدة مقارنة بوضع النافذة: إثبات أن الموقع الذي سيعرضه ينتمي إليك. هذا هو ما يمنع طرفاً ثالثاً من وضع حفلاتنا في إطار لخداع عملائك.

  • • أعلِن عن موقعك في الإعدادات → التوقيع المدمج، ثم انشر سجل DNS المشار إليه. عنوان دقيق: نطاق فرعي أو منفذ مختلف يُحتسب كموقع آخر.
  • • في كل توقيع، اطلب عنوان URL جديد من خلال POST /api/v1/envelopes/{id}/embed-url. يحمل صلاحية العرض وينتهي بعد ساعتين، وهذا يتوافق مع لحظة النقر، وليس عند إنشاء المغلف.
  • • لا يمكن لمسارين أن يعملا داخل إطار: التوقيع المؤهل، الذي يرفض التحقق من الهوية أن يُوضع هو الآخر في إطار، والدفع عند التوقيع. في كلا الحالتين يتم توجيه الموقِّع إلى نافذة كاملة.
  • • تختفي العلامة "مدعوم من Certyneo" من الإطار بدءاً من خطة Business. يتم تضمين وضع الإطار نفسه بدءاً من خطة Standard.
JavaScriptjavascript
// Mode cadre : la cérémonie s'affiche DANS votre page.
//
// 1. Déclarez votre site une fois pour toutes dans Réglages → Signature
//    intégrée, et publiez l'enregistrement DNS qu'on vous y donne.
//
// 2. À chaque signature, votre serveur demande une URL fraîche. Elle porte
//    l'autorisation qui permet au navigateur d'afficher la cérémonie chez
//    vous, et vaut deux heures :
//
//      POST /api/v1/envelopes/{id}/embed-url
//      { "origin": "https://portail.exemple.fr" }
//      -> { "signingUrl": "…", "expiresAt": "…" }

Certyneo.sign({
  mode: "iframe",
  el: "#zone-signature",        // sélecteur ou nœud de votre page

  getUrl: () =>
    fetch("/api/devis/42/signature", { method: "POST" })
      .then((r) => r.json())
      .then((d) => d.signingUrl),

  onCompleted: (e) => afficherConfirmation(e.envelopeId),
  onDeclined: () => afficherRefus(),
});

// Le cadre parle à votre page par postMessage — pas besoin de page de retour,
// contrairement au mode fenêtre.

السماح بالتوقيع دون إرسال أي بريد إلكتروني

بوابة تعرض الحفل عند المستقبِل غالبًا لا تحتاج إلى دعوتنا: تأتي بجانب مساره الخاص وتقترح حسابًا على عميل لا يريده. امنح قيمة NONE لقناة الإخطار الخاصة بمستقبِل وبعد ذلك لن يتم إرسال شيء له — أنت من تأخذه إلى توقيعه.

  • • الإعداد هو لكل مستقبِل، وليس لكل مظروف: يمكنك إسكات العميل الذي تستضيفه والاحتفاظ بالبريد الإلكتروني لموقّع خارجي، الذي ليس لديه بوابة.
  • • مخصص للموقّعين والمعتمدين، الوحيدين الذين لديك رابط لتسليمه. تقرأه من استجابة الإنشاء والإرسال، أو تطلب عنوان URL جديد عند النقر على POST /api/v1/envelopes/{id}/embed-url.
  • • إعادات المحاولة التلقائية تتوقف من تلقاء نفسها لهذا المستقبِل: لم نكتب له قط، لا نبدأ برسالة متابعة. يستمر باقي المستقبلين في تلقي إعادات المحاولة بشكل طبيعي.
  • • شهادة التوقيع تذكره، مستقبِل تلو الآخر. ملف إثبات يصمت بشأن ذلك قد يترك انطباعًا بأن هذا الموقّع تلقى منا دعوة لم تكن موجودة قط.
JSONjson
POST /api/v1/envelopes

{
  "subject": "Mandat de gestion",
  "documentIds": ["doc_8f2c1a4b9e7d"],
  "recipients": [
    {
      "email": "client@exemple.fr",
      "name": "Camille Client",
      "role": "SIGNER",
      "notificationChannel": "NONE"
    },
    {
      "email": "notaire@exemple.fr",
      "name": "Maître Notaire",
      "role": "SIGNER"
    }
  ]
}

// La réponse rend le lien de chaque signataire :
//
//   "recipients": [
//     { "email": "client@exemple.fr",  "accessToken": "…",
//       "notificationChannel": "NONE"  },
//     { "email": "notaire@exemple.fr", "accessToken": "…",
//       "notificationChannel": "EMAIL" }
//   ]
//
// Le client ne reçoit rien : vous l'emmenez sur sa signature depuis votre
// portail. Le notaire, lui, reçoit son invitation comme d'habitude.

عرض اسم عميلك كمرسل

بشكل افتراضي، يقرأ الموقّع اسم صاحب مفتاح API. إذا كنت تطلب توقيع المستندات من عدة شركات باستخدام مفتاح واحد، مرّر حقل المرسل عند إنشاء المظروف: ستعرض صفحة التوقيع ورسائل البريد الإلكتروني للدعوة والتذكير اسم الشركة المعنية.

  • • مخصص للخطة Business والخطط الأعلى. في الخطة الأدنى، يتم رفض الإنشاء بدلاً من ترك الموقّع يقرأ اسماً غير متوقع.
  • • الاسم مجرد عرض: يحتفظ الشهادة ومسار التدقيق بالمرسل الفعلي، وتأتي رسائل البريد الإلكتروني دائماً من Certyneo.
  • • العنوان اختياري. بدونه، لا يرى الموقّع سوى الاسم، مطلقاً لا عنوان صاحب المفتاح.
JSONjson
POST /api/v1/envelopes

{
  "subject": "Devis 2026-118",
  "documentIds": ["doc_8f2c1a4b9e7d"],
  "recipients": [
    { "email": "client@exemple.fr", "name": "Camille Client", "role": "SIGNER" }
  ],
  "sender": {
    "name": "1.2.3. Panneaux Solaires",
    "email": "devis@123-panneaux-solaires.fr"
  }
}

التحقق من الهوية

كل طلب يحتوي على مفتاح API في رأس الطلب Authorization. يتم إنشاؤها من الإعدادات → مفاتيح API ولا تظهر إلا مرة واحدة.

HTTPhttp
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.

المجموعة — مغلقةjson
// GET /api/v1/envelopes
// Collections are WRAPPED in a "data" array.
{
  "data": [
    { "id": "env_abc123", "subject": "Contrat", "status": "SENT" }
  ]
}
وحدة الموارد — مسطحةjson
// GET /api/v1/envelopes/{id}
// Single resources are returned FLAT — no "data" envelope.
{
  "id": "env_abc123",
  "subject": "Contrat",
  "status": "COMPLETED",
  "recipients": [ /* … */ ]
}

حدود معدل النقل

الحدود تضمن جودة خدمة مستقرة لجميع العملاء. إذا كنت بحاجة إلى المزيد، فاتصل بنا.

  • • 100 طلب في الدقيقة لكل مفتاح API
  • • رفقة مقبولة حتى 200 طلب في أقل من 10 ثوان
  • • استجابة 429 مع رأس Retry-After يشير إلى التأخير بالثواني

الذهاب أبعد من ذلك