Skip to content

Onboarding API: endpoints

Base URL Sandbox https://provider-dev.timologisi.online · Production https://provider.timologisi.online
Authentication Header API-KEY, the same key as for the Provider API. The key identifies which software house is calling. Never send it in the body or the URL.
Limit 60 calls / minute per key. The X-RateLimit-Remaining header shows how many remain. Above the limit: 429 RATE_LIMITED with Retry-After.
Dates Date-time in ISO-8601 UTC. Plain date as YYYY-MM-DD.
Files OpenAPI (YAML) · Postman
Endpoint What it does
POST /api/v1/requests Create a request for one client
GET /api/v1/requests List your requests
GET /api/v1/requests/{requestId} Request status
GET /api/v1/requests/{requestId}/history Change history
GET /api/v1/requests/{requestId}/contract Download the contract (PDF)
POST /api/v1/requests/{requestId}/signed-contract Upload the signed contract
DELETE /api/v1/requests/{requestId} Cancel a request
POST GET DELETE /api/v1/webhooks… Webhooks

Response format

All JSON endpoints wrap their response in an envelope:

{ "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." }
    ]
  }
}

The error object may also contain context (e.g. existingRequestId, currentStatus) and, only on 500, traceId.

Create a request

POST /api/v1/requests

Header
Idempotency-Key Optional. A GUID of your own, one per client, unchanged across all retries. If the response is lost and you resend with the same key, you get back the original request (200 instead of 201). Do not reuse it for another VAT number, or for a request that was cancelled or rejected: 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"
  }
}
Field Required Rules
companyDetails.legalName yes
companyDetails.tradeName
companyDetails.vatNumber yes 9 digits with a valid check digit. Test values such as 123456789 are rejected.
companyDetails.taxOffice yes
companyDetails.transactionTypes yes At least one of B2B, B2C. B2G is not accepted.
address.city, address.streetAddress yes
address.postalCode
contactInfo.email yes The contract is sent here.
contactInfo.phone yes E.164 (+30…) or a Greek 10-digit number
contactInfo.backupPhone Same format
administrator.fullName yes The legal representative
administrator.vatNumber yes A valid VAT number (check digit)
ispDetails.providerName, contractNumber, contractDate if B2C The client's internet service provider, all three fields. contractDate cannot be in the future. If B2B only, omit ispDetails.

All field errors are returned together in error.details[].

The system decides the request type. You do not send it:

  • NEW_CONTRACT: the VAT number has no active contract with Novus. A PDF contract is generated and the request goes to PENDING_SIGNATURE.
  • LINK_EXISTING: the VAT number already has an active contract through another software house. contract is null, no signature is needed, and the request goes straight to UNDER_REVIEW.

The client automatically receives an email with the contract and a link to upload it themselves. So the request can move to UNDER_REVIEW without any action on your side.

HTTP When
201 The request was created.
200 Retry with the same Idempotency-Key: the original request is returned.
409 DUPLICATE_OPEN_REQUEST: an open request already exists for the VAT number, and error.context.existingRequestId shows which one. CLIENT_ALREADY_LINKED: the VAT number is already active on your key. Or an Idempotency-Key was reused for another VAT number / for a cancelled or rejected request.
422 VALIDATION_ERROR: see 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" }
  }
}

List requests

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

Returns only your own requests.

Query Description
status One or more statuses, separated by commas
requestType NEW_CONTRACT or LINK_EXISTING
vatNumber Client VAT number
createdFrom, createdTo Date range (YYYY-MM-DD)
page From 1 (default 1)
pageSize 1 to 200 (default 50)
sort createdAt:desc (default), createdAt:asc, updatedAt:desc, updatedAt:asc

data is { "items": [ … ], "total": 137, "page": 1, "pageSize": 50 }.

Request status

GET /api/v1/requests/{requestId}

The actual status of the request. After APPROVED, provisioning and aadeStatement are filled in.

Field Meaning
status See the statuses.
message The latest message, e.g. why the request is in ACTION_REQUIRED.
contract Number, date, templateVersion, downloadUrl. After an upload, also signedVersion (increases with each upload) and sha256. In LINK_EXISTING it is null, and this is not an error.
provisioning After APPROVED. status: PENDING → IN_PROGRESS → COMPLETED or FAILED. Steps clientAdded, clientLinked, contractUploaded, statementSent, each PENDING, IN_PROGRESS, DONE or FAILED. Plus error.
aadeStatement The Provider declaration to AADE (the Greek tax authority). status: SUBMITTED, ACCEPTED (the client accepted it), PRESUMED_ACCEPTED (10 days passed with no action), RECALLED. Plus acceptDate.

When the client can start issuing

When provisioning.status = COMPLETED.

If you see FAILED, Novus has already been notified and is handling it. Do not send the request again.

Example: approved and activated request
{
  "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"
  }
}

Change history

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

A chronological list of changes. It is useful, for example, when the client uploaded the contract themselves. Each entry has:

Field
occurredAt When
status, previousStatus From which status to which
message Message
actor Who caused the change: SOFTWARE_HOUSE, CLIENT, NOVUS, SYSTEM

Download the contract

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

Returns application/pdf.

Query
kind unsigned (default) or signed
version Version of the signed file. Without it, the latest is returned.

Not a public link

It needs your API key. Do not send the downloadUrl to the client as is. Download the PDF and forward it yourself.

A LINK_EXISTING request has no contract: 409 NOT_APPLICABLE. The same applies when the version you asked for does not exist.

Upload the signed contract

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

multipart/form-data, one file in the contractFile field.

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"

Rules:

  • exactly one file,
  • a real PDF (the magic bytes are checked, not the extension), up to 15 MB, not corrupted or password-protected,
  • the request is in PENDING_SIGNATURE or ACTION_REQUIRED, with requestType = NEW_CONTRACT.

Old files are not deleted. Each upload is a new version. On success (200) the request moves to UNDER_REVIEW.

HTTP error.code When
400 BAD_REQUEST Invalid multipart, more than one file, or a corrupted / password-protected PDF
409 INVALID_TRANSITION Wrong status, e.g. a file is already under review
409 NOT_APPLICABLE The request is LINK_EXISTING
413 FILE_TOO_LARGE Over 15 MB
415 UNSUPPORTED_FILE_TYPE Not a PDF

The client can also upload the file themselves, from the link in the email.

Cancel a request

DELETE /api/v1/requests/{requestId}

Optional body { "reason": "…" } (up to 500 characters). Allowed only before approval. On a request that is already approved or in a final status: 409 INVALID_TRANSITION. Ending the cooperation after approval is possible only in agreement with Novus, not through the API.

Error codes

HTTP error.code Meaning
400 BAD_REQUEST Malformed JSON or multipart
401 UNAUTHORIZED The API key is missing or invalid
403 FORBIDDEN The action is not allowed with this key
404 NOT_FOUND It does not exist or belongs to another software house. 404 is returned instead of 403, so that its existence is not revealed.
409 DUPLICATE_OPEN_REQUEST An open request already exists for the VAT number (context.existingRequestId)
409 CLIENT_ALREADY_LINKED The VAT number is already active on your key
409 CLIENT_ALREADY_ACTIVE The VAT number got an active contract before approval
409 INVALID_TRANSITION Not allowed in the current status (context.currentStatus)
409 NOT_APPLICABLE Does not apply to this request type
413 FILE_TOO_LARGE File over 15 MB
415 UNSUPPORTED_FILE_TYPE Not a PDF
422 VALIDATION_ERROR Invalid fields, see details[]
429 RATE_LIMITED More than 60 calls / minute. Retry after Retry-After seconds.
500 INTERNAL_ERROR Unexpected error, with traceId. Send us the traceId.
502 UPSTREAM_ERROR Temporary failure of an external service. Retry later.