Tài liệu Open API tích hợp Hợp đồng số MISA weSign

I. Giới thiệu

Tài liệu này được cung cấp cho các đối tác của MISA muốn tích hợp với ứng dụng MISA WeSign.

Tài liệu đưa ra chủ yếu nhằm phục vụ việc tạo và xử lý các tệp phục vụ cho việc ký số sau này

1.1. Thông tin cần trước khi kết nối

Thông tin Giá trị Diễn giải
Đối tác/KH phải sử dụng dịch vụ MISA WeSign Liên hệ KD MISA.
Đăng ký sử dụng dịch vụ API Nhận về key x-client-id và x-client-secret và tài liệu mô tả về API
userName userName Tài khoản MISAID
password password Mật khẩu tài khoản
x-client-id Do MISA cung cấp

(Site demo: 

628e2f6d-d403-4ea3-bf4a-9dbb980c2600)

Là thông tin ClientID
x-client-secret Do MISA cung cấp

(Site demo: 

cc04ddf8-1f6d-4dfb-a2ea-188d80d8a24f)

Do MISA cung cấp
x-amis-sid Lấy được từ bước thi công Giá trị dùng để lấy token

1.2. Thông tin chi tiết để kết nối

Thông tin Diễn giải
UrlWesignAPI Product: https://amisapp.misa.vn/wesign/api/public

Test: https://testapiwesign.misa.vn

Demo: https://demoamisapp.misa.vn/wesign/api/public

Đăng ký sử dụng callback Xem hướng dẫn build callback (tham khảo response api documents/detail)

Đối tác/KH gửi link callback để Dự án Wesign thiết lập phản hồi

Tải về file mẫu callback

Lấy SID Build theo format

https://amisapp.misa.vn/login?clientID={x-client-id}&redirectUri={redirectUri}

VD: https://amisapp.misa.vn/login?clientID={x-client-id}&redirectUri={https://google.com}

Token url (lấy access token) api/v1/auth/credentials
Làm mới token api/v1/auth/refresh-token
Tải lên tệp api/File/upload
Tạo luồng ký documents/signflow
Lấy danh sách tài liệu mẫu documents/template
Lấy chi tiết tài liệu mẫu documents/templates/{id}
Tạo lô ký với dữ liệu trộn mẫu từ tệp excel documents/merge-batch-excel
Tạo lô ký với dữ liệu trộn mẫu từ json documents/merge-batch
Lấy thông tin trạng thái ký của tài liệu documents/detail

1.3. Mô tả cấu trúc API WeSign

Thông tin Diễn giải
Url Theo từng API cụ thể
Method Get/Post (Theo từng API cụ thể)
Params Theo từng API cụ thể
Header x-client-id: {x-client-id}

x-client-secret: {x-client-secret}

x-amis-sid: {sid}

Authorization: {access token}

Body Content-Type: application/json (Đối tượng theo từng API cụ thể)
Response Content-Type: application/json (Đối tượng theo từng API cụ thể)

1.4. Hướng dẫn xử lý kết quả của API (Response)

Response thành công Được mô tả ở mỗi API
Response lỗi Trả về cùng 1 định dạng, sau đây gọi là ResponseError

{

ErrorCode“: “Mã lỗi“,

DevMsg“: “mô tả thông tin lỗi cho người phát triển“,

UserMsg“: “mô tả thông tin lỗi cho người dùng“,

TraceId“: “Mã để tra cứu thông tin“,

MoreInfo“: “Thông tin khác“,

AccessToken“: “giá trị access token“,

}

1.5. Các bước thực hiện

Đăng ký sử dụng dịch vụ 
Đăng ký sử dụng API Đăng ký sử dụng API, liên hệ NVKD của MISA

Nhận về x-client-id và x-client-secret

Đăng nhập thông tin Khi thực hiện lấy sid, cần có tài khoản MISA ID (có sử dụng ứng dụng MISA Wesign)
Thao thác với API
Bước 1: Thực hiện lấy sid Xem mô tả
Bước 2: Thực hiện gọi API lấy access token Xem mô tả

Lưu ý:

– Token có hạn được trả về qua thông tin acccessExpAt

Bước 2.1: Làm mới access token (nếu cần) Xem mô tả
Tải lên tệp Xem mô tả
Tạo luồng ký Xem mô tả
Lấy danh sách tài liệu mẫu Xem mô tả
Lấy chi tiết tài liệu mẫu Xem mô tả
Tạo lô ký với dữ liệu trộn từ excel Xem mô tả
Tạo lô ký với dữ liệu trộn từ json Xem mô tả
Lấy trạng thái ký của tài liệu Xem mô tả
Postman tham khảo Tải về

II. API lấy Token

2.1. Lấy SID

Để lấy được SID, đơn vị tích hợp cần điều hướng qua url được xây dựng theo định dạng:

{UrlAmisPlatform}/login?clientID={clientID}&redirectUri={redirectUri}

 

Trong đó:

redirectUri: là uri của ứng dụng web (ví dụ https://google.com)

Kết quả trả về sẽ có định dạng

{redirectUri}/?sID={SID}&lid=lid=vi&env=DemoOnline

VD: https://www.google.com/?sid=srxGCqkj%2BanR&lid=vi&env=DemoOnline

2.2. Lấy token

Thông tin Diễn giải
URL {UrlWesignAPI}/api/v1/auth/credentials
Method Post
Headers {

    “x-client-id“: “{x-client-id}”,

    “x-client-secret“: “{x-client-secret}”,

    “x-amis-sid“: “{SID}”

}

Response {

    “accessToken“: “mã token”,

    “acccessExpAt“: “hạn token”,

    “refreshToken“: “mã làm mới token”,

    “refreshTokenExpAt“: “hạn của mã làm mới”

}

Lưu ý: Lưu lại thông tin {accessToken} để sử dụng cho các api phía sau

2.3. Làm mới token

Thông tin Diễn giải
URL {UrlWesignAPI}/api/v1/auth/refresh-token
Method Post
Headers {

    “x-refresh-token“: “{accessToken}”

}

Response Dữ liệu trả về tương tự API lấy token

III. API thao tác với tài liệu

3.1. Tải lên tệp và tạo luồng ký

3.1.1. Tải lên tệp

Khi call api này, với dữ liệu định nghĩa ở Body.Data sẽ trả về thông tin ký ở Response.positionSignatures

Thông tin Diễn giải
URL {UrlWesignAPI}/api/File/upload
Method Post
Headers {

    “Authorization“: “Bearer {accessToken}”

}

Body request type: multipart/form-data

{

   “Data”: “RequestUploadData

   “Files”: “@<path-to-file>” //đường dẫn file tài liệu

}

API sẽ tìm vị trí của các key để tạo ra thông tin ký trả về positionSignatures ở response

Response ResponseUpload

Lưu ý: Lưu lại thông tin của response để sử dụng cho các api phía sau

Mã lỗi thường gặp

3.1.2. Tạo luồng ký

Thông tin Diễn giải
URL {UrlWesignAPI}/Documents/signflow
Method Post
Headers {

    “Authorization“: “Bearer {accessToken}”

}

Body List<BodySignFlow>
Response List<ResponseSignflow>

Lưu ý: Lưu lại thông tin để sử dụng cho các api phía sau

Mã lỗi thường gặp

3.2. Lấy danh sách tài liệu mẫu

Thông tin Diễn giải
URL {UrlWesignAPI}/Documents/template
Method Post
Headers {

    “Authorization“: “Bearer {accessToken}”

}

Body {

        “Take“: 100, //số lượng bản ghi lấy ra

        “Skip“: 0, //số lượng bỏ qua

        “Keyword“: “từ khóa tìm kiếm theo tên tài liệu”

}

Response [

   {

        “id“: “id của tài liệu”,

        “name“: “tên tài liệu”

   }

]

Mã lỗi thường gặp
Lưu ý Lưu lại thông tin để sử dụng cho các api phía sau

3.3. Lấy chi tiết tài liệu mẫu

Thông tin Diễn giải
URL {UrlWesignAPI}/Documents/templates/{id}
Method Get
Headers {

    “Authorization“: “Bearer {accessToken}”

}

Response ResponseDocumentTemplatesDetail

Lưu ý: Lưu lại thông tin để sử dụng cho các api phía sau

Mã lỗi thường gặp

3.4. Tạo lô ký với dữ liệu trộn mẫu từ tệp excel

Thông tin Diễn giải
URL {UrlWesignAPI}/Documents/merge-batch-excel
Method Post
Headers {

    “Authorization“: “Bearer {accessToken}”

}

Body request type: multipart/form-data

{

        “Files“: “File excel chứa nội dung trộn”,

        “IsNotAllowCoordinatorEdit“: true/false, //Có cho người điều phối chỉnh sửa hay không

        “DocumentTemplateId“: “id của mẫu lấy từ danh sách mục 3.2″

}

Response {

    “success“: true/false, //trạng thái trộn

    “code“: “thông tin gửi về”,

    “message“: “thông báo”,

    “systemMessage“: “thông báo của hệ thống”,

    “data“: “tên tài liệu”,

    “subCode“: “tên tài liệu”,

    “userMessage“: “tên tài liệu”,

    “validateInfo“: “danh sách thông tin kiểm tra”

}

Mã lỗi thường gặp
Lưu ý Nếu thành công chỉ trả về success: true

Luồng ký sẽ được tạo và tự động gửi cho người cần ký

Các thông tin về luồng ký sẽ trả về callback

 

3.5. Tạo lô ký với dữ liệu trộn mẫu từ json

Thông tin Diễn giải
URL {UrlWesignAPI}/Documents/merge-batch
Method Post
Headers {

    “Authorization“: “Bearer {accessToken}”

}

Body {

        “documentID“: “File excel chứa nội dung trộn”,

        “batchName“: true/false, //Có cho người điều phối chỉnh sửa hay không

        “isNotAllowCoordinatorEdit“: “id của mẫu lấy từ danh sách

        “mergeDatas“: “danh sách mappingField” //lấy danh sách từ chi tiết tài liệu

}

Mỗi mappingField là 1 list các phần tử

{

      “mappingField”: “DocumentName”, // tên trường mapping

      “typeMappingField”: 0,

      “value”: “giá trị thay thế”

}

muốn tạo ra bao nhiêu tài liệu thì thêm từng đó list mappingField

Định nghĩa typeMappingField

0: kiểu của hệ thống (chỉ sử dụng với mappingField = DocumentName và AsyncID)

1: dùng cho thông tin ký của các bên (là thông tin jobPosition truyền vào mappingField lấy từ api Lấy chi tiết mẫu (yêu cầu truyền đúng text)

2: trường trộn

Response {

    “success“: true/false, //trạng thái trộn

    “code“: “tên tài liệu”,

    “message“: “tên tài liệu”,

    “systemMessage“: “tên tài liệu”,

    “data“: “tên tài liệu”,

    “subCode“: “tên tài liệu”,

    “userMessage“: “tên tài liệu”,

    “validateInfo“: “danh sách thông tin kiểm tra”

}

tương tự với trộn excel, nhưng thông tin các trường trộn được thể hiện ở body dưới dạng json

Mã lỗi thường gặp
Lưu ý Các thông tin về luồng ký sẽ trả về callback

3.6. Hủy tài liệu

API sẽ hủy tài liệu được tạo

Thông tin Diễn giải
URL {UrlWesignAPI}/Documents/cancel
Method Post
Headers {

    “Authorization“: “Bearer {accessToken}”

}

Body {

        “reason“: “Lý do hủy”,

        “docIds“: [Danh sách tài liệu hủy],

        “device“: “thông tin thiết bị hủy”

        “emailTemplateId“: “”

}

Response [Danh sách tài liệu hủy thành công]

Hoặc trường hợp lỗi

{

        “ErrorCode“: “mã lỗi”,

        “DevMsg“: “Diễn giải lỗi”,

        “UserMsg“: “Diễn giải lỗi”,

        “MoreInfo“: “thông tin khác nếu có”,

        “TraceId“: “”,

        “AccessToken“: “”,

        “ValidationFailures“: “”

}

Mã lỗi thường gặp
Lưu ý docIds có thể lấy từ API Lấy danh sách mẫu

 

3.7. Lấy thông tin trạng thái ký của tài liệu

Thông tin Diễn giải
URL {UrlWesignAPI}/Documents/detail
Method Post
Headers {

    “Authorization“: “Bearer {accessToken}”

}

Body [

   Danh sách id tài liệu

]

Response [

   {

        “document“: “Thông tin của tài liệu của api SignFlow”,

        “listDocumentParticipant“: “Danh sách cks có trong tài liệu”

   }

]

document.status: trạng thái ký của tài liệu

0: tài liệu chưa hoàn thành

1: tài liệu đã hoàn thành

3: tài liệu bị từ chối

5: tài liệu bị hủy bỏ

Mã lỗi thường gặp

 

IV. Email

4.1. Lấy danh sách mẫu email

Thông tin Diễn giải
URL {UrlWesignAPI}/api/EmailTemplate
Method Get
Header {

    “Authorization“: “Bearer {accessToken}”

}

Response [

{

        “listTemplateSignInvitation“: “Danh sách mẫu mail mời ký”,

        “listTemplateDistributeInvitation“: “Danh sách mẫu mail mời điều phối”,

        “listTemplateApprovalInvitation“: “Danh sách mẫu mail mời phê duyệt”,

        “listTemplateSignReject“: “Danh sách mẫu mail từ chối ký”,

        “listTemplateApprovalReject“: “Danh sách mẫu mail từ chối phê duyệt”,

        “listTemplateSignComplete“: “Danh sách mẫu mail hoàn thành tài liệu”

}

]

Đối tượng của các list có định dạng:

{

        “id“: “id của mẫu email”,

        “name“: “Tên mẫu email”,

        “content“: “nội dụng email dạng html”,

        “title“: “nội dụng email dạng html”,

        “dataMerge“: “nội dụng email dạng html”

}

Mã lỗi thường gặp

V. API thao tác với tệp

5.1. Tải xuống tệp theo ID

Thông tin Diễn giải
URL {UrlWesignAPI}/api/File/downloadFileInfos
Method Post
Headers {

    “Authorization“: “Bearer {accessToken}”

}

Body [id document] //danh sách id của tệp
Response {

        “fileContents“: “nội dung file dạng byte”,

        “contentType“: “định dạng file” //thường là file zip

}

Mã lỗi thường gặp
Lưu ý Cần save file theo contentType của API trả về

5.2. Tải xuống tệp của tài liệu đã ký

Thông tin Diễn giải
URL {UrlWesignAPI}/api/File/downloadFileInfosByDocumentID
Method Post
Headers {

    “Authorization“: “Bearer {accessToken}”

}

Body [id document] //danh sách id của tệp
Response {

        “fileContents“: “nội dung file dạng byte”,

        “contentType“: “định dạng file” //thường là file zip

}

Mã lỗi thường gặp
Lưu ý Response tương tự với API Tải xuống tệp theo ID

VI. Mô tả đối tượng

6.1. Đối tượng đầu vào

6.1.1. RequestUploadData

Tên trường Kiểu dữ liệu Bắt buộc Mô tả
listSign List<SignatureInfo> Danh sách vị trí đặt chữ ký

6.1.2. SignatureInfo

Tên trường Kiểu dữ liệu Bắt buộc Mô tả
key String Mã định danh vùng ký (ví dụ: ###Signature1###)
width Int Chiều rộng chữ ký (đơn vị pixel hoặc % nếu có)
height Int Chiều cao chữ ký (đơn vị pixel hoặc % nếu có)
participantid String (UUID) ID của người tham gia ký

6.1.3. BodySignFlow

Tên trường Kiểu dữ liệu Bắt buộc Mô tả
document Document Thông tin tài liệu
listFile List<FileInfo> Danh sách tài liệu đính kèm
listDocumentParticipant List<DocumentParticipant> Danh sách người tham gia ký
infoEnvelope.title String Tiêu đề email gửi đi
infoEnvelope.content String Nội dung email
infoEnvelope.expiredDate DateTime? Không Ngày hết hạn ký
infoEnvelope.listMessage List<ListMessage> Danh sách tin nhắn bổ sung (nếu có)
typeSaveSign Int 1: Mời người khác ký
device.deviceName String? Không Tên thiết bị
device.deviceOS String? Không Hệ điều hành thiết bị
device.location String? Không Vị trí người ký (GPS hoặc địa chỉ IP)
device.ipAddress String? Không Địa chỉ IP của người ký
isCheckHour Boolean Có kiểm tra thời gian ký không
folderID Int ID thư mục lưu trữ
emailConfig.senderAddress String Email người gửi thông báo
includeSignLink Boolean Có kèm đường dẫn ký trong mail không
disableNotification Boolean Có tắt thông báo không

6.1.4. Document

Tên trường Kiểu dữ liệu Bắt buộc Mô tả
name Tên/tiêu đề tài liệu hiển thị lên app khi tạo thành công
isOrderSign Boolean Có ký theo thứ tự không
isSendViaEmailAvailable Boolean Cho phép gửi thông báo qua email
appCode String Không Mã ứng dụng
appName String Không Tên ứng dụng
appSubSystem String Tên phân hệ ứng dụng
optionReSignTime DateTime? Không Thời gian được phép ký lại nếu có

6.1.5. FileInfo

Tên trường Kiểu dữ liệu Bắt buộc Mô tả
objectId String ID file sau khi upload
fileName String Tên file
listPositionSignature List<PositionSignature> Danh sách vị trí ký trong file

6.1.6. PositionSignature

Tên trường Kiểu dữ liệu Bắt buộc Mô tả
positionX Int Tọa độ X
positionY Int Tọa độ Y
width Int Chiều rộng chữ ký
height Int Chiều cao chữ ký
page Int Trang ký
priority Int Thứ tự ưu tiên vị trí ký
level Int Mức độ của người ký
isRequiredDigitalSignature Boolean Bắt buộc ký số hay không

6.1.7. DocumentParticipant

Tên trường Kiểu dữ liệu Bắt buộc Mô tả
participantId String (UUID) ID của người tham gia (lấy từ upload)
participantName String Tên người ký
participantEmail String Email người ký
participantMobile String Không Số điện thoại người ký
role Int Vai trò người ký

Mô tả Enum Role

action Int Không Hành động với tài liệu
Mô tả Enum Action
priority Int Thứ tự ký
password String? Không Mật khẩu truy cập nếu có
jobPosition String Không Chức vụ người ký
level Int Mức độ của người ký
infoIdentifyEkyc Object? Không Thông tin định danh eKYC nếu có
taxCode String Không Mã số thuế đơn vị nhận tài liệu (nếu dùng nội bộ thì k cần truyền)
isAllowRecipientCopyForward Boolean Cho phép người ký chuyển tiếp email

6.1.8. Body_Documents_merge-batch

Tên trường Kiểu dữ liệu Bắt buộc Mô tả
documentID String ID của tài liệu cần merge
batchName String Tên lô/tên nhóm dữ liệu
mergeDatas Mảng  Danh sách các nhóm dữ liệu để merge
└─ mappingField String Tên trường mapping
└─ typeMappingField Integer Loại trường mapping
└─ value String Giá trị tương ứng với mappingField

6.1.9. ListMessage

Tên trường Kiểu dữ liệu Bắt buộc Mô tả
participantId string ID người nhận tin nhắn
message string Nội dung tin nhắn
temporaryMessageEnvelopeID string ID tạm để maping tin nhắn riêng từng vị trí ký

6.2. Đối tượng đầu ra

6.2.1. ResponseUpload

Tên trường Kiểu dữ liệu Mô tả
objectId String ID của tài liệu
positionSignatures Mảng Danh sách vị trí đặt chữ ký
└─ positionX Int Tọa độ X trên tài liệu
└─ positionY Int Tọa độ Y trên tài liệu
└─ width Int Chiều rộng chữ ký
└─ height Int Chiều cao chữ ký
└─ page Int Trang số trong tài liệu
isSuccess Boolean Trạng thái xử lý file
index Int Thứ tự tài liệu (nếu trong danh sách có nhiều)
validateResult Object? Kết quả kiểm tra hợp lệ (nếu có)
fileSize Int Kích thước file (bytes)
fileName String Tên file
numberOfPages Int Số trang của tài liệu

6.2.2. ResponseSignflow

Tên trường Kiểu dữ liệu Mô tả
index Int Vị trí trong danh sách nếu gửi nhiều tài liệu
isSuccess Boolean Trạng thái thành công hay không của tài liệu
fileInfos Mảng Danh sách thông tin các file liên quan đến tài liệu
└─ fileId String (UUID) ID file do hệ thống gán
└─ objectId String ID file đã upload trước đó (_encrypt cho biết file đã được mã hóa)
└─ listPositionSignFlow Object / null Danh sách vị trí ký (nếu có, thường null nếu không tìm thấy KEY thể hiện ký)
documentId String (UUID) ID của tài liệu ký đã được tạo
validateResult Object / null Kết quả kiểm tra hợp lệ tài liệu nếu có
participantSignLinks Mảng Danh sách người ký và đường dẫn ký
└─ participantName String Tên người tham gia ký
└─ participantEmail String Email người tham gia ký
└─ participantLink String (URL) Đường dẫn để người ký truy cập và ký tài liệu

6.2.3. ResponseDocumentTemplatesDetail

Tên trường Kiểu dữ liệu Mô tả
participantId String (UUID) ID định danh người ký
participantName String Họ tên người ký
participantEmail String Email người nhận ký
participantMobile String Số điện thoại người nhận ký
jobPosition String Chức danh trong hợp đồng (VD: Người lao động, Người sử dụng lao động)

Các job đặc thù

Họ và tên (khách hàng): Họ tên người nhận ký

Email (khách hàng): Email người nhận ký

Số điện thoại (khách hàng): số điện thoại gửi SMS đến người nhận ký

level Int Mức thứ tự ký (1, 2, …) nếu isOrderSign = true
priority Int 0 = người ký chính, có thể có các giá trị khác nếu hệ thống mở rộng
role Int Vai trò của người tham gia

0: người ký, 

1: người nhận bản sao, 

2: Người điều phối, 

3: Người phê duyệt

password String / null Mật khẩu ký (nếu có)
typePassword Int 0 = không cần mật khẩu, các giá trị khác có thể tương ứng xác thực khác
typeLanguage Int 0 = tiếng Việt, 

1 = tiếng Anh,…

participantMobile / mobile String / null Số điện thoại người ký
emailOTP String OTP qua email nếu dùng xác thực ký
isParticularPerson Boolean True nếu là cá nhân (khác tổ chức)
documentParticipantID String (UUID) ID người ký trong tài liệu
infoIdentifyEkyc / typeIdentifyEkyc Object / Int Dành cho định danh eKYC nếu có
message String Ghi chú riêng cho người ký
hasNoInfo Boolean true nếu người ký không có đủ thông tin ban đầu
listMergeFieldValue List<string> Danh sách các biến chèn vào tệp

6.3. Mô tả Enum

Thông tin Mô tả
Action Trạng thái ký:

0: Không làm gì cả

1: Đang chờ người này ký

2: Đã ký

3: Đã từ chối

4: Đang chờ điều phối

5: Đã điều phối

6: Đang chờ phê duyệt 

7: Đã phê duyệt 

8: Đã ủy quyền 

9: Đã bị hủy ủy quyền

Role 0: Người ký

1: Người nhận bản sao

2: Người điều phối

3: Người phê duyệt

VII. Mã lỗi thường gặp

Mã lỗi Mô tả Phương án xử lý
Liên quan đến trạng thái của Request
200 ok Cho biết request đã thành công. Chú ý: Với phương thức GET đồng nghĩa với việc dữ liệu trả về đã nằm trong response -> Client không phải handle gì thêm và có thể sử dụng luôn dữ liệu trả về.
201 created Tạo dữ liệu thành công. Sử dụng cho phương thức POST.
400 bad request Server không hiểu cú pháp của request. Chú ý: Trong cả các trường hợp validate thông tin input lỗi cũng trả về lỗi này.
401 unauthorized Hệ thống chưa được ủy quyền.
403 forbidden Server từ chối yêu cầu ngay cả khi Client đã được ủy quyền)
404 not found Server không tìm thấy bất kì tài nguyên nào liên quan tới request URL này.
500 internal server error Có lỗi xảy ra phía máy chủ.
Liên quan đến API hệ thống MISA Wesign
Auth:001 Thông tin xác thực không hợp lệ. Liên hệ kỹ thuật MISA
Auth:002 Token không hợp lệ. Kiểm tra thông tin tài khoản (liên hệ kinh doanh MISA)
Auth:003 Refresh token không hợp lệ. Kiểm tra thông tin tài khoản (liên hệ kinh doanh MISA)
Auth:004 Thiếu thông tin xác thực. Liên hệ kỹ thuật MISA
Business:001 License hết hạn. Mua thêm tài nguyên – Liên hệ KD MISA
Business:002 Hết số lượng tài liệu. Mua thêm tài nguyên – Liên hệ KD MISA
Business:003 Tài liệu không tồn tại. License SMS không có hoặc đã hết, cần bổ sung email cho người tham gia (mã này trả về qua callback)