์ฃผ์š” ์ฝ˜ํ…์ธ ๋กœ ์ด๋™
Certyneo
๊ณต๊ฐœ API v1

์ „์ž์„œ๋ช…์„ ์Šคํƒ์— ํ†ตํ•ฉ

๋ด‰ํˆฌ๋ฅผ ๋ฐœ์†กํ•˜๊ณ , ์„œ๋ช…์„ ์ถ”์ ํ•˜๋ฉฐ, ์›นํ›…์„ ์ˆ˜์‹ ํ•ฉ๋‹ˆ๋‹ค. ๊ฐ„๋‹จํ•œ REST API, OpenAPI 3.0, curl/Node/Python ์˜ˆ์ œ โ€” ๋ช‡ ์‹œ๊ฐ„ ๋‚ด์— Certyneo๋ฅผ HRIS, CRM ๋˜๋Š” ์—…๋ฌด ์†Œํ”„ํŠธ์›จ์–ด์— ์—ฐ๊ฒฐํ•  ์ˆ˜ ์žˆ๋Š” ๋ชจ๋“  ๊ฒƒ.

๋น ๋ฅธ ์‹œ์ž‘

3๋‹จ๊ณ„: ์„ค์ •์—์„œ 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
// 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);
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"])

๋ด‰ํˆฌ

์ƒ์„ฑ, ๋ฐœ์†ก, ์ƒํƒœ ์ถ”์ , ์ทจ์†Œ. ํ•˜๋‚˜์˜ ๋ด‰ํˆฌ๋Š” ์—ฌ๋Ÿฌ ๋ฌธ์„œ์™€ ์—ฌ๋Ÿฌ ์„œ๋ช…์ž(๋ณ‘๋ ฌ ๋˜๋Š” ์ˆœ์ฐจ)๋ฅผ ํฌํ•จํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์›นํ›…

`envelope.created`, `envelope.completed`, `envelope.declined`์„ ์„ ํƒํ•œ URL๋กœ ์ˆ˜์‹ ํ•ฉ๋‹ˆ๋‹ค. ์ถœ์ฒ˜ ํ™•์ธ์„ ์œ„ํ•ด ๊ฐ ํŽ˜์ด๋กœ๋“œ์— HMAC SHA-256์„ ์ ์šฉํ•ฉ๋‹ˆ๋‹ค.

๊ฐ„๋‹จํ•œ ์ธ์ฆ

Bearer ํ† ํฐ. ํ™˜๊ฒฝ๋ณ„ 1๊ฐœ ํ‚ค(ํ…Œ์ŠคํŠธ/ํ”„๋กœ๋•์…˜). ์ฆ‰์‹œ ์ทจ์†Œ ๊ฐ€๋Šฅ. ์ดˆ๋‹น 100 ์š”์ฒญ/๋ถ„/ํ‚ค ์ œํ•œ, ์ตœ๋Œ€ 200์˜ ๋ฒ„์ŠคํŠธ, Retry-After ํ—ค๋”๊ฐ€ ํฌํ•จ๋œ ๊น”๋”ํ•œ 429 ์‘๋‹ต.

์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ์—”๋“œํฌ์ธํŠธ

12๊ฐœ์˜ ๊ฒฝ๋กœ๋กœ ์™„์ „ํ•œ ์‚ฌ์ดํด ์ปค๋ฒ„: envelopes, documents, webhooks, API ํ‚ค. ๋ชจ๋“  ๊ฒฝ๋กœ๋Š” Bearer ํ† ํฐ์„ ํ—ˆ์šฉํ•˜๊ณ  JSON์„ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

MethodPathDescription
GET/api/v1/account/meIdentity of the authenticated caller (id, email, plan) โ€” scope-less credential probe
POST/api/v1/documentsUpload a PDF (multipart) โ€” returns document id
GET/api/v1/documentsList documents
GET/api/v1/documents/{id}Fetch document metadata
DELETE/api/v1/documents/{id}Delete document
GET/api/v1/envelopesList envelopes (filter with ?status= and ?limit=)
POST/api/v1/envelopesCreate envelope (status: DRAFT)
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}/sendDispatch DRAFT โ€” sends invitations
GET/api/v1/envelopes/{id}/audit-trailDownload eIDAS audit-trail PDF
GET/api/v1/envelopes/{id}/signed-documentDownload signed PDF (once COMPLETED)
GET/api/v1/templatesList reusable envelope templates
GET/api/v1/webhooksList webhooks
POST/api/v1/webhooksRegister 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/sealsApply a qualified electronic seal to a document
GET/api/v1/seals/{id}Fetch seal status
GET/api/v1/seals/{id}/certificateDownload the seal certificate
GET/api/v1/keysList API keys
POST/api/v1/keysCreate 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/usageCurrent period usage and projected cost
GET/api/v1/statusService status
GET/api/v1/openapiMachine-readable OpenAPI specification

Authentification

Chaque appel porte une clรฉ API dans l'en-tรชte Authorization. Les clรฉs se gรฉnรจrent depuis Rรฉglages โ†’ Clรฉs API et ne sont affichรฉes qu'une seule fois.

HTTPhttp
GET /api/v1/account/me HTTP/1.1
Host: certyneo.com
Authorization: Bearer sk_live_xxxxxxxxxxxxxxxxxxxxxxxx

# 200 OK
{ "data": { "id": "usr_โ€ฆ", "email": "you@example.com", "plan": "BUSINESS", "environment": "live" } }
  • โ€ข Format : sk_live_โ€ฆ en production, sk_test_โ€ฆ pour le bac ร  sable. En-tรชte : Authorization: Bearer <clรฉ>.
  • โ€ข Portรฉes : envelopes, documents, webhooks, seals โ€” en lecture (:read) ou รฉcriture (:write). L'รฉcriture implique la lecture ; la portรฉe * donne tous les droits.
  • โ€ข Les clรฉs sk_test_ crรฉent des ressources en bac ร  sable, exclues du quota et de la facturation.
  • โ€ข Erreurs : 401 clรฉ invalide, 403 portรฉe insuffisante, 429 limite de dรฉbit dรฉpassรฉe, 402 quota mensuel atteint.

Forme des rรฉponses

Un point ร  connaรฎtre avant d'รฉcrire votre client : les collections sont encapsulรฉes dans un objet data, alors que les ressources unitaires sont renvoyรฉes ร  plat. Lire response.data.data sur une ressource unitaire renvoie donc undefined.

Collection โ€” encapsulรฉejson
// GET /api/v1/envelopes
// Collections are WRAPPED in a "data" array.
{
  "data": [
    { "id": "env_abc123", "subject": "Contrat", "status": "SENT" }
  ]
}
Ressource unitaire โ€” ร  platjson
// GET /api/v1/envelopes/{id}
// Single resources are returned FLAT โ€” no "data" envelope.
{
  "id": "env_abc123",
  "subject": "Contrat",
  "status": "COMPLETED",
  "recipients": [ /* โ€ฆ */ ]
}

์š”์ฒญ ์ œํ•œ

์ œํ•œ์€ ๋ชจ๋“  ํด๋ผ์ด์–ธํŠธ๋ฅผ ์œ„ํ•œ ์•ˆ์ •์ ์ธ ์„œ๋น„์Šค ํ’ˆ์งˆ์„ ๋ณด์žฅํ•ฉ๋‹ˆ๋‹ค. ๋” ๋†’์€ ํ•œ๋„๊ฐ€ ํ•„์š”ํ•˜์‹œ๋ฉด ์ €ํฌ์—๊ฒŒ ๋ฌธ์˜ํ•˜์„ธ์š”.

  • โ€ข API ํ‚ค๋‹น ๋ถ„๋‹น 100๊ฐœ ์š”์ฒญ
  • โ€ข 10์ดˆ ์ด๋‚ด์— ์ตœ๋Œ€ 200๊ฐœ ์š”์ฒญ๊นŒ์ง€ ๋ฒ„์ŠคํŠธ ํ—ˆ์šฉ
  • โ€ข ์žฌ์‹œ๋„ ์ง€์—ฐ(์ดˆ)์„ ๋‚˜ํƒ€๋‚ด๋Š” Retry-After ํ—ค๋”๊ฐ€ ํฌํ•จ๋œ 429 ์‘๋‹ต