Μετάβαση στο περιεχόμενο

Onboarding API: endpoints

Base URL Δοκιμαστικό https://provider-dev.timologisi.online · Παραγωγή https://provider.timologisi.online
Αυθεντικοποίηση Header API-KEY, το ίδιο κλειδί με το Provider API. Το ποιο software house καλεί προκύπτει από το κλειδί. Δεν στέλνεται ποτέ στο body ή στο URL.
Όριο 60 κλήσεις / λεπτό ανά κλειδί. Το header X-RateLimit-Remaining δείχνει πόσες απομένουν. Πάνω από το όριο: 429 RATE_LIMITED με Retry-After.
Ημερομηνίες Ημερομηνία-ώρα σε ISO-8601 UTC. Σκέτη ημερομηνία σε YYYY-MM-DD.
Αρχεία OpenAPI (YAML) · Postman
Endpoint Τι κάνει
POST /api/v1/requests Δημιουργία αίτησης για έναν πελάτη
GET /api/v1/requests Λίστα των αιτήσεών σας
GET /api/v1/requests/{requestId} Κατάσταση αίτησης
GET /api/v1/requests/{requestId}/history Ιστορικό μεταβολών
GET /api/v1/requests/{requestId}/contract Λήψη της σύμβασης (PDF)
POST /api/v1/requests/{requestId}/signed-contract Ανέβασμα της υπογεγραμμένης σύμβασης
DELETE /api/v1/requests/{requestId} Ακύρωση αίτησης
POST GET DELETE /api/v1/webhooks… Webhooks

Η μορφή κάθε απάντησης

Όλα τα JSON endpoints απαντούν μέσα σε φάκελο:

{ "success": true, "data": {  } }
{
  "success": false,
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Ο έλεγχος των πεδίων απέτυχε.",
    "details": [
      { "field": "companyDetails.vatNumber", "code": "INVALID_VALUE", "message": "companyDetails.vatNumber must be a valid nine digit Greek VAT number." }
    ]
  }
}

Στο error μπορεί να υπάρχουν επίσης το context (π.χ. existingRequestId, currentStatus) και, μόνο σε 500, το traceId.

Δημιουργία αίτησης

POST /api/v1/requests

Header
Idempotency-Key Προαιρετικό. Ένα GUID δικό σας, ένα ανά πελάτη, σταθερό σε όλα τα retries. Αν χαθεί η απάντηση και ξαναστείλετε με το ίδιο key, παίρνετε πίσω την αρχική αίτηση (200 αντί για 201). Μην το ξαναχρησιμοποιήσετε για άλλο ΑΦΜ, ή για αίτηση που ακυρώθηκε ή απορρίφθηκε: 409.
Body (B2B + B2C)
{
  "companyDetails": {
    "legalName": "Παπαδόπουλος Α.Ε.",
    "tradeName": "PapaCorp",
    "vatNumber": "094019245",
    "taxOffice": "Α΄ Αθηνών",
    "transactionTypes": [
      "B2B",
      "B2C"
    ]
  },
  "address": {
    "city": "Αθήνα",
    "streetAddress": "Ερμού 15",
    "postalCode": "10563"
  },
  "contactInfo": {
    "email": "info@papacorp.gr",
    "phone": "2101234567",
    "backupPhone": "6971234567"
  },
  "administrator": {
    "fullName": "Γιάννης Παπαδόπουλος",
    "vatNumber": "090000045"
  },
  "ispDetails": {
    "providerName": "Cosmote",
    "contractNumber": "CSM-2026-1",
    "contractDate": "2026-09-01"
  }
}
Πεδίο Υποχρεωτικό Κανόνες
companyDetails.legalName ναι
companyDetails.tradeName
companyDetails.vatNumber ναι 9 ψηφία με σωστό check digit. Δοκιμαστικά όπως το 123456789 απορρίπτονται.
companyDetails.taxOffice ναι
companyDetails.transactionTypes ναι Τουλάχιστον ένα από B2B, B2C. Το B2G δεν γίνεται δεκτό.
address.city, address.streetAddress ναι
address.postalCode
contactInfo.email ναι Εδώ στέλνεται η σύμβαση.
contactInfo.phone ναι E.164 (+30…) ή ελληνικό 10ψήφιο
contactInfo.backupPhone Ίδια μορφή
administrator.fullName ναι Ο νόμιμος εκπρόσωπος
administrator.vatNumber ναι Έγκυρος ΑΦΜ (check digit)
ispDetails.providerName, contractNumber, contractDate αν B2C Ο πάροχος internet του πελάτη, και τα τρία πεδία. Το contractDate δεν μπορεί να είναι μελλοντικό. Αν μόνο B2B, το ispDetails παραλείπεται.

Όλα τα λάθη πεδίων επιστρέφονται μαζί στο error.details[].

Τον τύπο της αίτησης τον αποφασίζει το σύστημα, δεν τον στέλνετε εσείς:

  • NEW_CONTRACT: ο ΑΦΜ δεν έχει ενεργή σύμβαση με τη Novus. Παράγεται σύμβαση σε PDF και η αίτηση πάει σε PENDING_SIGNATURE.
  • LINK_EXISTING: ο ΑΦΜ έχει ήδη ενεργή σύμβαση μέσω άλλου software house. Το contract είναι null, δεν χρειάζεται υπογραφή, και η αίτηση πάει κατευθείαν σε UNDER_REVIEW.

Ο πελάτης λαμβάνει αυτόματα email με τη σύμβαση και σύνδεσμο για να την ανεβάσει μόνος του. Άρα η αίτηση μπορεί να περάσει σε UNDER_REVIEW χωρίς δική σας ενέργεια.

HTTP Πότε
201 Η αίτηση δημιουργήθηκε.
200 Επανάληψη με το ίδιο Idempotency-Key: επιστρέφεται η αρχική αίτηση.
409 DUPLICATE_OPEN_REQUEST: υπάρχει ήδη ανοιχτή αίτηση για τον ΑΦΜ, το error.context.existingRequestId δείχνει ποια. CLIENT_ALREADY_LINKED: ο ΑΦΜ είναι ήδη ενεργός στο κλειδί σας. Ή επαναχρησιμοποίηση Idempotency-Key για άλλο ΑΦΜ / για ακυρωμένη ή απορριφθείσα αίτηση.
422 VALIDATION_ERROR: δείτε το details[].
{
  "success": true,
  "data": {
    "requestId": "req_8f7b2c9a",
    "requestType": "NEW_CONTRACT",
    "status": "PENDING_SIGNATURE",
    "message": "Η αίτηση καταχωρήθηκε. Απαιτείται υπογραφή της σύμβασης από τον πελάτη.",
    "contract": {
      "contractNumber": "10000",
      "contractDate": "2026-09-17",
      "templateVersion": "v6",
      "downloadUrl": "https://provider.timologisi.online/api/v1/requests/req_8f7b2c9a/contract"
    },
    "provisioning": null,
    "aadeStatement": null,
    "createdAt": "2026-09-17T08:42:00Z"
  }
}
{
  "success": true,
  "data": {
    "requestId": "req_3a1c7d02",
    "requestType": "LINK_EXISTING",
    "status": "UNDER_REVIEW",
    "message": "Το ΑΦΜ έχει ήδη ενεργή σύμβαση. Η αίτηση σύνδεσης εστάλη για έλεγχο.",
    "contract": null,
    "provisioning": null,
    "aadeStatement": null,
    "createdAt": "2026-09-17T08:42:00Z"
  }
}
{
  "success": false,
  "error": {
    "code": "DUPLICATE_OPEN_REQUEST",
    "message": "Υπάρχει ήδη ανοιχτή αίτηση για αυτό το ΑΦΜ.",
    "context": { "existingRequestId": "req_8f7b2c9a" }
  }
}

Λίστα αιτήσεων

GET /api/v1/requests?status=UNDER_REVIEW,ACTION_REQUIRED&page=1&pageSize=50

Επιστρέφει μόνο τις δικές σας αιτήσεις.

Query Περιγραφή
status Ένα ή περισσότερα status, χωρισμένα με κόμμα
requestType NEW_CONTRACT ή LINK_EXISTING
vatNumber ΑΦΜ πελάτη
createdFrom, createdTo Διάστημα ημερομηνιών (YYYY-MM-DD)
page Από 1 (default 1)
pageSize 1 έως 200 (default 50)
sort createdAt:desc (default), createdAt:asc, updatedAt:desc, updatedAt:asc

Το data είναι { "items": [ … ], "total": 137, "page": 1, "pageSize": 50 }.

Κατάσταση αίτησης

GET /api/v1/requests/{requestId}

Η πραγματική κατάσταση της αίτησης. Μετά το APPROVED συμπληρώνονται τα provisioning και aadeStatement.

Πεδίο Τι σημαίνει
status Δείτε τις καταστάσεις.
message Το τελευταίο μήνυμα, π.χ. γιατί η αίτηση είναι σε ACTION_REQUIRED.
contract Αριθμός, ημερομηνία, templateVersion, downloadUrl. Μετά το ανέβασμα και signedVersion (αυξάνεται σε κάθε ανέβασμα) και sha256. Σε LINK_EXISTING είναι null, και αυτό δεν είναι σφάλμα.
provisioning Μετά το APPROVED. status: PENDINGIN_PROGRESSCOMPLETED ή FAILED. Βήματα clientAdded, clientLinked, contractUploaded, statementSent, το καθένα PENDING, IN_PROGRESS, DONE ή FAILED. Και error.
aadeStatement Η Δήλωση Παρόχου στην ΑΑΔΕ. status: SUBMITTED, ACCEPTED (την αποδέχτηκε ο πελάτης), PRESUMED_ACCEPTED (πέρασαν 10 ημέρες χωρίς ενέργεια), RECALLED. Και acceptDate.

Πότε ο πελάτης μπορεί να εκδίδει

Όταν provisioning.status = COMPLETED.

Αν δείτε FAILED, η Novus έχει ήδη ειδοποιηθεί και το αναλαμβάνει. Μην ξαναστείλετε αίτηση.

Παράδειγμα: εγκεκριμένη και ενεργοποιημένη αίτηση
{
  "success": true,
  "data": {
    "requestId": "req_8f7b2c9a",
    "requestType": "NEW_CONTRACT",
    "status": "APPROVED",
    "message": "Η αίτηση εγκρίθηκε.",
    "contract": {
      "contractNumber": "10000",
      "contractDate": "2026-09-17",
      "templateVersion": "v6",
      "signedVersion": 1,
      "sha256": "9f2c1d6e8a0b4c7d5e3f1a2b8c9d0e4f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d",
      "downloadUrl": "https://provider.timologisi.online/api/v1/requests/req_8f7b2c9a/contract"
    },
    "provisioning": {
      "status": "COMPLETED",
      "clientAdded": "DONE",
      "clientLinked": "DONE",
      "contractUploaded": "DONE",
      "statementSent": "DONE",
      "error": null
    },
    "aadeStatement": { "status": "SUBMITTED", "acceptDate": null },
    "createdAt": "2026-09-17T08:42:00Z",
    "updatedAt": "2026-09-17T11:03:00Z"
  }
}

Ιστορικό μεταβολών

GET /api/v1/requests/{requestId}/history

Χρονολογική λίστα των αλλαγών. Είναι χρήσιμη π.χ. όταν ο πελάτης ανέβασε μόνος του τη σύμβαση. Κάθε εγγραφή έχει:

Πεδίο
occurredAt Πότε
status, previousStatus Από ποιο status σε ποιο
message Μήνυμα
actor Ποιος προκάλεσε την αλλαγή: SOFTWARE_HOUSE, CLIENT, NOVUS, SYSTEM

Λήψη της σύμβασης

GET /api/v1/requests/{requestId}/contract?kind=unsigned

Επιστρέφει application/pdf.

Query
kind unsigned (default) ή signed
version Έκδοση του υπογεγραμμένου. Χωρίς αυτό επιστρέφεται η τελευταία.

Δεν είναι δημόσιο link

Χρειάζεται το API key σας. Μη στείλετε το downloadUrl στον πελάτη ως έχει. Κατεβάστε το PDF και προωθήστε το εσείς.

Σε αίτηση LINK_EXISTING δεν υπάρχει σύμβαση: 409 NOT_APPLICABLE. Το ίδιο και όταν δεν υπάρχει η έκδοση που ζητήσατε.

Ανέβασμα της υπογεγραμμένης σύμβασης

POST /api/v1/requests/{requestId}/signed-contract

multipart/form-data, ένα αρχείο στο πεδίο contractFile.

curl -X POST "https://provider-dev.timologisi.online/api/v1/requests/req_8f7b2c9a/signed-contract" \
  -H "API-KEY: $NOVUS_API_KEY" \
  -F "contractFile=@signed-contract.pdf;type=application/pdf"

Κανόνες:

  • ακριβώς ένα αρχείο,
  • πραγματικό PDF (ελέγχονται τα magic bytes, όχι η κατάληξη), έως 15 MB, όχι κατεστραμμένο ή κλειδωμένο με κωδικό,
  • η αίτηση σε PENDING_SIGNATURE ή ACTION_REQUIRED, με requestType = NEW_CONTRACT.

Τα παλιά αρχεία δεν σβήνονται. Κάθε ανέβασμα είναι νέα έκδοση. Με επιτυχία (200) η αίτηση περνά σε UNDER_REVIEW.

HTTP error.code Πότε
400 BAD_REQUEST Λάθος multipart, περισσότερα από ένα αρχεία, ή κατεστραμμένο / κλειδωμένο PDF
409 INVALID_TRANSITION Λάθος status, π.χ. υπάρχει ήδη αρχείο υπό έλεγχο
409 NOT_APPLICABLE Η αίτηση είναι LINK_EXISTING
413 FILE_TOO_LARGE Πάνω από 15 MB
415 UNSUPPORTED_FILE_TYPE Δεν είναι PDF

Ο πελάτης μπορεί να ανεβάσει το αρχείο και μόνος του, από το link του email.

Ακύρωση αίτησης

DELETE /api/v1/requests/{requestId}

Προαιρετικό body { "reason": "…" } (έως 500 χαρακτήρες). Επιτρέπεται μόνο πριν την έγκριση. Σε αίτηση που είναι ήδη εγκεκριμένη ή σε τελική κατάσταση: 409 INVALID_TRANSITION. Η διακοπή συνεργασίας μετά την έγκριση γίνεται μόνο σε συνεννόηση με τη Novus, όχι από το API.

Κωδικοί σφαλμάτων

HTTP error.code Τι σημαίνει
400 BAD_REQUEST Κακοδιατυπωμένο JSON ή multipart
401 UNAUTHORIZED Λείπει ή είναι άκυρο το API key
403 FORBIDDEN Η ενέργεια δεν επιτρέπεται με αυτό το κλειδί
404 NOT_FOUND Δεν υπάρχει ή ανήκει σε άλλο software house. Επιστρέφεται 404 αντί για 403, ώστε να μη φαίνεται καν ότι υπάρχει.
409 DUPLICATE_OPEN_REQUEST Υπάρχει ήδη ανοιχτή αίτηση για τον ΑΦΜ (context.existingRequestId)
409 CLIENT_ALREADY_LINKED Ο ΑΦΜ είναι ήδη ενεργός στο κλειδί σας
409 CLIENT_ALREADY_ACTIVE Ο ΑΦΜ απέκτησε ενεργή σύμβαση πριν την έγκριση
409 INVALID_TRANSITION Δεν επιτρέπεται στο τρέχον status (context.currentStatus)
409 NOT_APPLICABLE Δεν ισχύει για αυτόν τον τύπο αίτησης
413 FILE_TOO_LARGE Αρχείο πάνω από 15 MB
415 UNSUPPORTED_FILE_TYPE Δεν είναι PDF
422 VALIDATION_ERROR Λάθος πεδία, δείτε το details[]
429 RATE_LIMITED Πάνω από 60 κλήσεις / λεπτό. Ξαναδοκιμάστε μετά από Retry-After δευτερόλεπτα.
500 INTERNAL_ERROR Απρόσμενο σφάλμα, με traceId. Δώστε μας τον traceId.
502 UPSTREAM_ERROR Προσωρινή αποτυχία εξωτερικής υπηρεσίας. Ξαναδοκιμάστε αργότερα.