Imports API
Μαζική φόρτωση επαφών, συμβάντων ή εγγραφών προσαρμοσμένων οντοτήτων από αρχείο CSV ή JSON.
Όλα τα endpoints αυτής της σελίδας είναι σχετικά με τη βασική διεύθυνση: https://api-eu1.joryio.com/api - δείτε Επισκόπηση API.
Επισκόπηση
Μια εισαγωγή είναι τρεις κλήσεις, όχι μία. Ζητάτε θέση μεταφόρτωσης, ανεβάζετε το αρχείο απευθείας στον αποθηκευτικό χώρο, και μόνο τότε ζητάτε από το Joryio να το επεξεργαστεί:
POST /users/import/upload-url- επιστρέφει προϋπογεγραμμένο URL και τοfileKeyπου ονομάζει τη μεταφόρτωσή σας.PUTτου αρχείου σε αυτό το URL - απευθείας στον αποθηκευτικό χώρο, ώστε ένα αρχείο 200 MB να μην περνά ποτέ από το API.POST /users/import- δημιουργεί την εργασία. Επιστρέφει αμέσως· το αρχείο επεξεργάζεται στο παρασκήνιο.GET /users/import- ερώτημα για την πρόοδο και τα τελικά νούμερα.
Η επιτυχία του βήματος 2 δεν λέει τίποτα για το βήμα 3. Η μεταφόρτωση είναι απλώς ένα αρχείο που προσγειώθηκε σε έναν κάδο· η επικύρωση γίνεται κατά τη δημιουργία της εργασίας και ξανά ανά γραμμή κατά την εκτέλεση.
Έλεγχος ταυτότητας
Authorization: Bearer YOUR_API_KEY
Κάθε endpoint εδώ απαιτεί users:write. Η ρύθμιση overrideConsent απαιτεί επιπλέον compliance:write - το users:write από μόνο του παίρνει 403 και η εισαγωγή δεν ξεκινά.
1. Αίτημα URL μεταφόρτωσης
POST /users/import/upload-url
Content-Type: application/json
{
"filename": "customers-2026-08.csv"
}
{
"success": true,
"fileKey": "imports/<organizationId>/<workspaceId>/customers-2026-08.csv",
"uploadUrl": "https://storage.example.com/..."
}
Το fileKey περιορίζεται στον οργανισμό και τον χώρο εργασίας σας. Επιστρέψτε το αυτούσιο στο βήμα 3 - ένα κλειδί εκτός του δικού σας προθέματος απορρίπτεται με 400.
2. Μεταφόρτωση του αρχείου
curl -X PUT --upload-file customers-2026-08.csv "$UPLOAD_URL"
Ένα 200 εδώ σημαίνει ότι τα bytes αποθηκεύτηκαν. Δεν σημαίνει ότι η εισαγωγή θα πετύχει.
3. Δημιουργία της εργασίας εισαγωγής
POST /users/import
Content-Type: application/json
{
"format": "csv",
"fileKey": "imports/<organizationId>/<workspaceId>/customers-2026-08.csv",
"importType": "users",
"originalFilename": "customers-2026-08.csv",
"fieldMapping": {
"Email Address": "email",
"First Name": "firstName",
"Loyalty Tier": "attributes.loyaltyTier"
}
}
| Πεδίο | Τύπος | Σημειώσεις |
|---|---|---|
format | csv | json | Υποχρεωτικό |
fileKey | string | Υποχρεωτικό. Ακριβώς ό,τι επέστρεψε το βήμα 1 |
importType | users | events | entities | Προεπιλογή users |
originalFilename | string? | Εμφανίζεται στο ιστορικό εισαγωγών |
fieldMapping | object? | Στήλη προέλευσης → διαδρομή πεδίου προορισμού. Παραλείψτε το για αυτόματο εντοπισμό από τη γραμμή κεφαλίδων |
overrideConsent | boolean? | Δείτε παρακάτω. Απαιτεί compliance:write |
smsAlreadyConfirmed | boolean? | Μόνο για double opt-in SMS. Δείτε παρακάτω. Απαιτεί compliance:write |
maxBackdatingDays | int? | 1-3650. Πόσο πίσω μπορεί να χρονολογηθεί ένα εισαγόμενο συμβάν |
entityName | string? | Υποχρεωτικό όταν το importType είναι entities |
Άγνωστα πεδία απορρίπτονται με 400 αντί να αγνοούνται, ώστε ένα τυπογραφικό λάθος σε όνομα πεδίου να αποτυγχάνει θορυβωδώς αντί να μην κάνει τίποτα σιωπηλά.
Το fieldMapping κρατά στήλες, και μόνο στήλες
Κάθε κλειδί στο fieldMapping είναι ένα δικό σας όνομα στήλης. Οι επιλογές εισαγωγής είναι ξεχωριστά, τυποποιημένα πεδία - overrideConsent, entityName, maxBackdatingDays - και ποτέ κλειδιά σε αυτόν τον χάρτη.
Αυτό έχει σημασία αν έχετε στήλη που ονομάζεται κυριολεκτικά _overrideConsent: είναι στήλη, αντιστοιχίζεται όπως κάθε άλλη, και δεν ενεργοποιεί την παράκαμψη συναίνεσης. Μόνο το τυποποιημένο πεδίο το κάνει αυτό.
Παραλείψτε τελείως το fieldMapping και οι στήλες θα αντιστοιχιστούν από τη γραμμή κεφαλίδων. Δώστε το και θα ληφθεί ακριβώς όπως δόθηκε - ό,τι δεν αντιστοιχιστεί αγνοείται.
Χωρίς fieldMapping, οι συνηθισμένες γραφές κεφαλίδων αντιστοιχίζονται στα τυπικά πεδία - για παράδειγμα first_name, First Name ή given_name στο attributes.firstName, surname ή last_name στο attributes.lastName, e-mail στο email και mobile στο phone· κάθε άλλη στήλη γίνεται προσαρμοσμένο χαρακτηριστικό με το δικό της όνομα. Η πλήρης λίστα βρίσκεται στη Μαζική εισαγωγή. Το full_name δεν χωρίζεται. Τα αρχεία CSV μπορούν να έχουν διαχωριστικό κόμμα, ερωτηματικό ή tab - το διαχωριστικό διαβάζεται από τη γραμμή κεφαλίδων.
Αρχεία JSON
Και τα δύο σχήματα γίνονται δεκτά:
overrideConsent
Από προεπιλογή, μια εισαγωγή μπορεί να αποκλείσει αλλά όχι να παραχωρήσει: μια γραμμή unsubscribed στο αρχείο σας διαγράφει την επαφή, ενώ μια γραμμή subscribed δεν επανεγγράφει κάποιον που είχε προηγουμένως εξαιρεθεί.
Το overrideConsent: true αντιμετωπίζει το αρχείο ως αυθεντικό και προς τις δύο κατευθύνσεις. Είναι ενέργεια συμμόρφωσης, οπότε απαιτεί compliance:write πέραν του users:write.
Προσέξτε την ασυμμετρία: οι διαγραφές ισχύουν ούτως ή άλλως. Δεν χρειάζεστε αυτήν τη σημαία για να συγχρονίσετε εξαιρέσεις από ένα CRM - μόνο για να επανεγγράψετε άτομα που είχαν εξαιρεθεί.
Η τιμή πρέπει να είναι boolean JSON. Ένα string, ένα 1 ή ένα "yes" απορρίπτεται με 400 αντί να διαβαστεί σιωπηλά ως false - είναι καλύτερο να σας το πουν παρά να πιστεύετε ότι έτρεξε μια παράκαμψη που δεν έτρεξε.
Διπλότυπες επαφές και αποκλεισμοί
Ακόμη και με overrideConsent: true, μια επανεγγραφή ισχύει μόνο για την επαφή της γραμμής - άλλες επαφές με το ίδιο email ή τηλέφωνο μένουν διαγραμμένες - μια διαγραφή STOP παρακάμπτεται μόνο με το flag (καταγράφεται στο ιστορικό της επαφής με την εργασία εισαγωγής), και μια εισαγωγή δεν αίρει ποτέ hard bounce, καταγγελία spam ή μπλοκάρισμα σε επίπεδο οργανισμού. Χωρίς αυτό, μια τιμή subscribed για μπλοκαρισμένη επαφή αγνοείται. Μια τιμή unsubscribed φτάνει σε κάθε επαφή του workspace με το ίδιο email ή τηλέφωνο. Επαφές που μοιράζονται email ή τηλέφωνο.
Η ολοκληρωμένη εργασία περιέχει consentSummary:
{ "overridden": 12, "overriddenSmsStop": 3, "keptUnsubscribed": 40, "keptDeliverability": 1, "keptOrganization": 0 }
Το overridden μετρά επανεγγραφές με overrideConsent, και το overriddenSmsStop πόσες από αυτές ήταν επαφές SMS που είχαν στείλει STOP (ο πάροχος SMS μπορεί να συνεχίσει να μπλοκάρει μηνύματα μέχρι να στείλουν START)· το keptUnsubscribed μετρά τιμές subscribed που αγνοήθηκαν επειδή το flag ήταν ανενεργό· τα keptDeliverability και keptOrganization μετρούν όσες δεν μπορεί να άρει καμία εισαγωγή. Το consentSummary είναι null για εισαγωγές που ολοκληρώθηκαν πριν υπάρξει.
smsAlreadyConfirmed
Έχει σημασία μόνο όταν το double opt-in για SMS του χώρου εργασίας είναι ενεργό. Τότε κάθε νέα εγγραφή SMS του αρχείου (επαφή χωρίς κατάσταση SMS) αποθηκεύεται ως pending και λαμβάνει ένα μήνυμα "Reply YES", μέσα σε περίπου ένα λεπτό· το consentSummary.awaitingSmsConfirmation τις μετρά. Το smsAlreadyConfirmed: true δηλώνει ότι έχετε απόδειξη πως αυτά τα άτομα έχουν ήδη επιβεβαιώσει την εγγραφή τους στα SMS: αποθηκεύονται ως subscribed χωρίς μήνυμα. Είναι δήλωση συμμόρφωσης, οπότε απαιτεί compliance:write όπως το overrideConsent.
Με overrideConsent: true (και ενεργό double opt-in SMS), μια επαφή του αρχείου που έκανε διαγραφή από τα SMS αντιμετωπίζεται ως νέα εγγραφή: pending, ΕΝΑ μήνυμα επιβεβαίωσης, χωρίς υπενθύμιση, εγγραφή μόνο μετά το YES, επιστροφή σε unsubscribed χωρίς απάντηση. Χωρίς αυτό, οι διαγραμμένες επαφές μένουν αμετάβλητες και δεν λαμβάνουν μήνυμα. Το smsAlreadyConfirmed: true τις εγγράφει αντί γι' αυτό (μαζί με overrideConsent). Επαφή που είναι ήδη pending δεν επιβεβαιώνεται ποτέ από αρχείο.
4. Ερώτημα για την πρόοδο
GET /users/import
Επιστρέφει το ιστορικό εισαγωγών του χώρου εργασίας, από το νεότερο, με status, totalRows, processedRows, createdCount, updatedCount, skippedCount, failedCount και, όταν υπάρχουν αποτυχημένες γραμμές, ένα περιορισμένο δείγμα των σφαλμάτων με τους αριθμούς γραμμών προέλευσης. Το matchedSeveralCount μετρά γραμμές με email αλλά χωρίς externalId, όταν πολλές επαφές μοιράζονται αυτό το email: κάθε τέτοια γραμμή ενημέρωσε μόνο την πιο πρόσφατα ενεργή από αυτές, μια απεγγραφή της έφτασε σε όλες, και η αναφορά σφαλμάτων την καταγράφει ως notice. Δείτε Όταν πολλές επαφές μοιράζονται ένα email· προσθέστε externalId στις γραμμές για ακριβή αντιστοίχιση μίας επαφής.
Το status κινείται pending → processing → completed ή failed.
5. Λίστα εισαγωγών που μπορεί να στοχεύσει ένα τμήμα
GET /users/import/sources?limit=100
Απαιτεί users:read. Επιστρέφει τις ολοκληρωμένες εισαγωγές επαφών (users) του χώρου εργασίας, από τη νεότερη - τη λίστα από την οποία επιλέγει η συνθήκη τμήματος Προήλθε από εισαγωγή:
{
"imports": [
{
"id": "6f1c2a9e-4b7d-4c1e-9a53-2f0d8e7b1c44",
"fileName": "customers-2026-08.csv",
"status": "completed",
"createdAt": "2026-10-08T09:00:00.000Z",
"completedAt": "2026-10-08T09:05:12.000Z",
"rowCount": 1200,
"createdCount": 1000,
"updatedCount": 150
}
]
}
Το limit είναι 100 από προεπιλογή και το πολύ 200. Το ids (χωρισμένα με κόμμα, έως 100) επιστρέφει ακριβώς αυτές τις εισαγωγές, όσο παλιές κι αν είναι - για να ονομάσετε τις εισαγωγές στις οποίες δείχνει μια αποθηκευμένη συνθήκη. Το status είναι completed, expired (ολοκληρώθηκε· η αναφορά σφαλμάτων της έχει καθαριστεί) ή failed - μια αποτυχημένη εισαγωγή εμφανίζεται μόνο αν πρόλαβε να επεξεργαστεί γραμμές, γιατί οι επαφές που έφτασε είναι σημαδεμένες. Οι εισαγωγές συμβάντων και οντοτήτων δεν εμφανίζονται.
Κάθε επαφή που δημιουργεί ή ενημερώνει μια εισαγωγή επαφών σημαδεύεται με το id της εισαγωγής, ώστε ένα τμήμα να μπορεί να στοχεύσει την εισαγωγή:
{ "type": "contact_import", "operator": "any_of", "value": ["6f1c2a9e-4b7d-4c1e-9a53-2f0d8e7b1c44"] }
Δείτε το φίλτρο contact_import στο Segments API. Οι επαφές που εισήχθησαν πριν κυκλοφορήσει αυτή η δυνατότητα δεν φέρουν σημάδι.
Μία εισαγωγή τη φορά
Ένας χώρος εργασίας μπορεί να έχει μία εισαγωγή σε pending ή processing. Ένα δεύτερο αίτημα ενώ μία είναι ενεργή επιστρέφει:
{ "statusCode": 400, "message": "An import is already in progress for this workspace. Please wait for it to complete." }
Κάντε ερώτημα στο βήμα 4 μέχρι να ολοκληρωθεί η πρώτη, αντί να ξαναπροσπαθήσετε αμέσως.
Σφάλματα
| Κατάσταση | Σημασία |
|---|---|
| 400 | Άγνωστο πεδίο, λανθασμένη τιμή overrideConsent, fileKey εκτός του προθέματός σας, εισαγωγή entities χωρίς entityName, ή εισαγωγή που ήδη τρέχει |
| 403 | Ζητήθηκε overrideConsent χωρίς compliance:write |
| 403 | Ζητήθηκε smsAlreadyConfirmed χωρίς compliance:write |
| 404 | Το fileKey δεν αντιστοιχεί σε μεταφορτωμένο αντικείμενο - η εργασία δημιουργείται και μετά αποτυγχάνει με "The specified key does not exist" |
Γραμμές που αποτυγχάνουν μεμονωμένα δεν ρίχνουν την εργασία. Μετρώνται στο failedCount και δειγματίζονται στην αναφορά σφαλμάτων, ώστε ένα αρχείο με τρεις κακές γραμμές στις πενήντα χιλιάδες να εισάγει τις υπόλοιπες 49.997.