Welcome
Welcome to the Coinify Wallet Trading API documentation.
In this document we will take you through the process of integrating with Coinify to let your customers buy and sell cryptocurrencies through the API.
Getting started
We have made the API with simplicity in mind and we want it to be as simple as possible to get started - The following procedure describes the simplified flow that needs to be implemented in order to let a user buy and sell crypto.
- Register and/or authenticate user
- Create a price quote
- Create a trade
- Pay for trade
Here is an overview of the flows recommend to implement in order to get started.
- Buy flow
- Sell flow
- Resume unfinished trade
- KYC / Customer identity verification
Views to implement
In order to create a full API integration with Coinify you need to implement the following views.
- Quote
- Order summary
- Bank account registration
- Credit card payment
- Historical trades
- KYC / Customer identity verification
Introduction
This is the documentation for Coinify’s Wallet Trading API.
Environments
The following environments are available
Environment | Base URL | BTC Blockchain |
---|---|---|
Production | https://app-api.coinify.com | Mainnet |
Sandbox | https://app-api.sandbox.coinify.com | Testnet3 |
Request/response format
All timestamps used in requests and responses are in ISO 8601 format.
Request format
Example HTTP POST request to request trade price quote
POST /trades/quote HTTP/2
Host: app-api.coinify.com
User-Agent: ExampleIntegration/1.0
Content-Type: application/json;charset=utf-8
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
{"baseAmount":-100,"baseCurrency":"EUR","quoteCurrency":"BTC"}
Data payloads in requests must be encoded using JSON, and accompanied by a Content-Type: application/json
HTTP header.
See an example request to the right.
User agent
Example User-Agent header for (fictitious) GlobalCoin integration on Android:
User-Agent: GlobalCoin+Android/2.4.2-r4
In order for Coinify to easily track which API endpoints are being used by which partners - and to assist in integration troubleshooting,
we strongly suggest that you supply a custom User-Agent
HTTP header with a user agent string that is unique to your implementation.
As some parts of our API also read the user agent to optimize the experience for a specific platform, please also add Android
or iOS
to
the user agent if you know that your end-user will be using one of those platforms.
Response format
The API always responds with a HTTP status code and usually a JSON object in the response body. This JSON object carries the data of the response, or an error object which provides more information about the error other than the HTTP status code. If the endpoint doesn’t return any data, the response body is empty.
You can determine whether a call was successful or not by checking the HTTP status code.
Success format
The body of a successful response (HTTP status code is 2xx) will always be a JSON object or an empty body (if no data needs to be returned).
The contents of the response JSON object depends entirely on the endpoint and the type of operation performed.
Error format
In addition to the JSON response body seen below, a failed request always returns with a 4xx or 5xx HTTP status code.
{
"error": "api_key_required",
"error_description": "Please provide a valid API key for this operation.",
"request_id": "132baa4e-dd07-4a9a-981a-0932815e570c"
}
Whenever a request fails (the HTTP status code is 4xx or 5xx), the API returns a JSON object with two or three strings:
error
- Contains a machine-readable error codeerror_description
- Contains a human-readable error messagerequest_id
- Contains an ID of the request used for troubleshooting
Signup
Currently, only trader signup is supported.
The signup endpoints reside under https://app-api.coinify.com/signup
.
Trader signup
Minimal example request for
POST /signup/trader
{
"partnerId": "5f51bfe2-d08a-4b43-9d5c-405fd2f2ede6",
"email": "trader@example.com",
"profile": {
"address": {
"country": "DK"
}
}
}
Maximal example request for
POST /signup/trader
{
"partnerId": "5f51bfe2-d08a-4b43-9d5c-405fd2f2ede6",
"email": "trader@example.com",
"password": "mypassword",
"accountType": "individual",
"profile": {
"address": {
"state": "CA",
"country": "US"
}
},
"generateOfflineToken": true,
"consentAdditionalMails": true
}
Example response for
POST /signup/trader
{
"trader": {
"id": 754035
},
// Offline token, only included if requested explicitly, and only in the signup response.
"offlineToken": "aGFja2VydHlwZXIuY29tIGlzIG15IElERQ=="
}
POST https://app-api.coinify.com/signup/trader
This endpoint allows you to sign up a new trader
Request object
The parameters in the request object are as follows:
Parameter | Type | Default | Description |
---|---|---|---|
email |
String | Required | Email address of the new trader (and user) |
password |
String | null |
The password of the user used in combination with the email address to sign in. If omitted or null , user cannot sign in until they manually reset their password. |
partnerId |
UUID | Required | Provide a Coinify partner ID to associate this trader with a specific Coinify partner. |
accountType |
String | individual |
Set account type if signing up as a business, can be corporate (will default to individual ) |
profile |
Object | null |
Object with additional information about the trader. |
→address |
Object | null |
Object with information about the trader’s physical address. |
→state |
String | null |
ISO 3166-2 state/province. Required if country is ‘US’ |
→country |
String | Required | ISO 3166-1 alpha-2 country code. |
trustedEmailValidationToken |
String | null |
(Only for trusted Coinify partners, partnerId must be provided) Signed message from partner for automatic validation of email . See Trusted email validation. |
generateOfflineToken |
Boolean | false |
Also generate and return an offline token for the new trader. If set to true , an offline token is created for the new trader and returned in the response with the key offlineToken . |
consentAdditionalMails |
Boolean | Required | Determines whether the user consents to receive additional email from Coinify. |
Response object
The success response object contains the following fields:
Key | Type | Description |
---|---|---|
trader |
Object | See example |
offlineToken |
String | (Optional) An offline token for the new trader. Only created and returned if the generateOfflineToken request parameter is explicitly set to true |
Error object
If the signup request fails for some reason, the response contains the first error encountered during the signup process:
HTTP status code | Error code | Description |
---|---|---|
201 | N/A |
Sign up successful |
400 | invalid_request |
There was something wrong with the signup request. See the error_description argument for a specific, human-readable error message. |
400 | email_address_and_partner_id_in_use |
The provided email address is already associated with an existing trader that belongs to the specified partner. |
400 | invalid_trusted_email_validation_token |
The supplied trustedEmailValidationToken was invalid. |
500 | internal_error |
An internal error happened during signup. Please try again later. |
Email validation
When signing up a trader with Coinify, we have to ensure that the email address provided is valid and that the new trader can receive emails sent to that address. This can happen in one of two ways:
- Manual validation: An email is sent to the specified address containing a 6-digit verification code that the user must provide in order to validate their email address.
- Trusted validation (Only for Coinify partners): The partner cryptographically signs a message stating that the partner has validated the email address previously. This signed message must be provided in the signup request. The email address is then immediately trusted as valid by proxy.
Trusted validation
While the manual validation described previously is easier to implement, it sometimes doesn’t make sense to perform an additional email validation from a user experience perspective - if the user is signing up for Coinify through a partner, the partner will most likely already have validated the user’s email address. In this case, the partner can issue a signed message to Coinify stating that the user’s email address has already been validated by the partner.
Coinify then trusts the partner to have validated the email address themselves, and the email address is immediately marked as validated, and the user won’t have to perform any manual action to validate the email address to Coinify.
Example of 2048-bit RSA public key in PEM format.
Coinify must have the partner’s public key (RSA/EC) in advance for trusted validation to work
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3joQFkg6LXuR2PvFDiZn
fOlF5edR4nEUxTErLlvjg00cus/MvgP8QwKhVYMlfNqEqa2N6vWeLyT0reJ8KKBl
6M7BKnxRmPwh0dx6S8E+tdHMoryjwopCDUlPpxoB8lw9/aRkFm4tLKzHH53Ypijl
9U+4M28KQHUnSq+rfskNXI7bgN1jIX7CAECqjQLExNsu7iAit5RsUBCiEb+p+HVy
mQ0S30CQJy93NT5AuWTrWKiNv1rUXYZ6YWODWLNCZ+GHJG6U0sV36EooFGmxadpH
UVo4HGMPEHNu07HK0CYlJgRF1h6z3ea6uCHVap4F6Kwa4chdKJm+H6ukCElPGaW1
YwIDAQAB
-----END PUBLIC KEY-----
In essence, for trusted validation to work, the partner must have generated a cryptographic key pair and sent the public key to Coinify. Before the signup, the partner then signs a message containing the same email address as the email
parameter in the signup. If the email address of the signed message equals that of the signup request and the signature of the message is valid using the public key, Coinify considers the email address as valid. If the email addresses differ or the signature is invalid, the signup request will fail with a invalid_trusted_email_validation_token
error code.
Signed message details
The signed message containing the email address must be a JSON Web Token (JWT) and passed in the signup request as the trustedEmailValidationToken
parameter.
The message must be signed using one of the following algorithms:
JWT alg name |
Algorithm |
---|---|
RS256 |
RSASSA using SHA-256 hash algorithm |
RS384 |
RSASSA using SHA-384 hash algorithm |
RS512 |
RSASSA using SHA-512 hash algorithm |
ES256 |
ECDSA using P-256 curve and SHA-256 hash algorithm |
ES384 |
ECDSA using P-384 curve and SHA-384 hash algorithm |
ES512 |
ECDSA using P-521 curve and SHA-512 hash algorithm |
JWT Header
Example JWT header using
RS256
algorithm
{
"alg": "RS256",
"typ": "JWT"
}
The header must contain exactly two values (see example to the right):
Key | Type | Description |
---|---|---|
typ |
String | JWT identifier. Must contain the string "JWT" . |
alg |
String | Signature algorithm. Must be one of the allowed signature algorithms listed above. |
JWT payload
Example JWT payload for email address
johndoe@example.com
{
"email": "johndoe@example.com",
"exp": 1467331200 // Expires at 2016-07-01 00:00:00 UTC
}
The payload of the JWT can contain the following values (see example to the right):
Key | Type | Default value | Description |
---|---|---|---|
email |
String | Required | Email address. The valid email address. |
exp |
Integer | Never expire | Expiration time. Unix timestamp for when this JWT will no longer be valid. If given, the message will only be valid for validating the email address up until the given time. If not given, the message will always be valid. |
JWT example
Example JWT token (with added line break)
eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.
eyJlbWFpbCI6ImpvaG5kb2VAZXhhbXBsZS5jb20iLCJleHAiOjE0NjczMzEyMDB9.
qpn5iey9H86OwxsH8npw_IkmRIE3nxJKgl3FqRP0lSjc5XCA71XcMppwU2WqELgu
Cl7arKSmiHdp9GTu_sfXTk1bSiZMYfpfO810y02l_IrSh4YcZeYko3wW2QpVfgZF
lqwoNQcBM878mveUAmRKfhtQjohHcjLb5YDS2uVf2AFvXFUS443HL25_DC1IuUzr
YCv9pMBI4YHVxWIPU25EERMA03pa0O1XoF9LJR4uTBmKcQTC94IDbbf5GKz29Htk
kEvmx9z5cl0TZGxsRSwzsF0xI6v1taGzKo0NCn8AXpXkjiuffHm_Ml_v5L-bHGhZ
EYs25682iuS6K9Nl1FAggw
The above JWT contains the following header and payload. The token is signed with the private key matching the public key example shown previously.
// Header
{
"alg": "RS256",
"typ": "JWT"
}
// Payload
{
"email": "johndoe@example.com",
"exp": 1467331200 // Expires at 2016-07-01 00:00:00 UTC
}
Authentication
In order to call most of the API endpoints, one needs to obtain an access token. This token is currently obtainable by authenticating yourself to the system.
This section describes how to do just that.
Example
Authorization
header with an access token
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
Once you have obtained your access token, you must include it in all requests that require authentication. You do so by adding a Authorization
header to the request with the value Bearer <access-token>
as seen in the example on the right.
Failure to include an access token when accessing protected resources will result in a 401 Unauthorized
response, while a 403 Forbidden
means that you don’t have access to the protected resource.
Authenticate
Example success response body
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ",
"token_type": "bearer",
"expires_in": 1200, // Lifetime of access token in seconds
"refresh_token": "wt5RoH8i6HkSQvI8kFpEBLEIB6lw8lOpYKHEz0ND9znDaAOtH1dFI32GqhvT9PGC"
}
POST https://app-api.coinify.com/auth
In order to obtain an access token, you must authenticate yourself using one of the authentication methods.
When a successful authentication has been performed, you will receive an access token and a refresh token as exemplified in the response to the right. You must use this access token to authenticate yourself to most other endpoints.
You must include a grant_type
parameter (string) in the request which defines the authentication method.
The authentication endpoint currently supports the following grant_type
s:
password
- Email/passwordrefresh_token
- Refresh access tokenoffline_token
- Offline token
The possible response types, regardless of the grant_type
used, are listed in the following table:
HTTP Response code | JSON data |
---|---|
200 OK |
Success, Response with a fresh authentication token. See example to the right. |
400 Bad request |
Error, see OAuth2 Error Response for possible error codes. |
Email authentication
Example request with
grant_type == "password"
{
"grant_type": "password",
"email": "user@coinify.com",
"partnerId": "5f51bfe2-d08a-4b43-9d5c-405fd2f2ede6",
"password": "password"
}
Use this grant_type
to authenticate a user with a combination of email address and password.
Request parameter | Type | Description |
---|---|---|
grant_type |
string | password - Authenticate using email and password. |
partnerId |
string | Id of the partner the user belongs to. |
email |
string | Email address of user to authenticate. |
password |
string | User’s password. |
Refresh access token
Example request with
grant_type == "refresh_token"
{
"grant_type": "refresh_token",
"refresh_token": "wt5RoH8i6HkSQvI8kFpEBLEIB6lw8lOpYKHEz0ND9znDaAOtH1dFI32GqhvT9PGC"
}
The access token that you received upon authentication is only valid for 20 minutes, while the refresh token is valid for 24 hours. After the access token has expired, you can obtain a new one by either (1) re-authenticating or by (2) refreshing your token. This endpoint allows you to refresh your token without requiring the user to re-authenticate. For this reason, the Log in/Log out button is automatically removed from the trade widget menu to provide a better user experience.
Request parameter | Type | Description |
---|---|---|
grant_type |
string | refresh_token - Refresh using a refresh token. |
refresh_token |
string | The refresh token obtained from the previous authentication response. |
Offline token
Example request with
grant_type == "offline_token"
{
"grant_type": "offline_token",
"offline_token": "A3873gbnvgHgniehfq8QHkSQvI8kFpEBsiudhAS83Nat2g7ASB2GqhvT9PGC"
}
Example response for authentication request
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ",
"refresh_token": "wt5RoH8i6HkSQvI8kFpEBLEIB6lw8lOpYKHEz0ND9znDaAOtH1dFI32GqhvT9PGC",
"token_type": "bearer",
"expires_in": 1200 // Lifetime of access token in seconds
}
The offline token is used exactly as you would use a refresh token, except that it doesn’t expire.
This token does not expire (it’s life-long).
Request parameter | Type | Description |
---|---|---|
grant_type |
string | offline_token - Authenticate using an offline token, that does not expire. |
offline_token |
string | The offline token obtained upon signup. |
Sign out (Revoke refresh token)
Example request
{
"token_type_hint": "refresh_token",
"token": "wt5RoH8i6HkSQvI8kFpEBLEIB6lw8lOpYKHEz0ND9znDaAOtH1dFI32GqhvT9PGC"
}
POST https://app-api.coinify.com/auth/revoke
If the user wants to sign out, the client will have to revoke the refresh token and “forget” the current access token. This endpoint allows you to revoke a refresh token, so it is no longer valid.
Request parameter | Type | Description |
---|---|---|
token_type_hint |
string | Must be refresh_token |
token |
string | The refresh token obtained from the authentication response |
The possible response types are listed in the following table:
HTTP Response code | JSON data |
---|---|
204 No Content |
Success, Empty response body |
400 Bad request |
Error, see OAuth2 Error Response for possible error codes. |
Deactivate account
PUT https://user.coinify.com/me/deactivate
If you wish to deactivate your account, you can make the above request. The only expected payload is the Authentication
HTTP Header.
You can only deactivate your own account.
A deactivated account can not obtain an access token and thus is unable to use the parts of the API that require an access token.
Response
HTTP Response code | JSON data |
---|---|
204 No Content |
Success, Empty response body |
500 Server Error |
Error, fulfilling the request. |
Partner
Approximate quote
Example of query parameters
{
"baseAmount": -100,
"baseCurrency": "EUR",
"quoteCurrency": "BTC",
"transferInMedium": "card",
"transferOutMedium": "blockchain"
}
Example response
{
"baseAmount": -100, // Requested amount that customer specified to send
"baseCurrency": "EUR", // Base currency that customer specified to send
"quoteAmount": 0.0012814, // The Approximate Quote amount.
"quoteCurrency": "BTC", // Currency the customer specified to receive
"transferIn": { // Object with details of the incoming transaction method (from customer)
"medium": "card", // Incoiming transaction type
"feeAmount": 4.99, // Incoming transfer fee. Added on top of "baseAmount" on checkout. I.e. customer would pay 104.99 EUR total in this example.
"currency": "EUR" // Incoming currency
},
"transferOut": { // Object with details of the outgoing transaction method (to customer)
"medium": "blockchain", // Outgoing transaction type
"feeAmount": 0.000195, // Outgoing transaction fee. Already included in the "quoteAmount".
"currency": "BTC" // Outgoing currency
}
}
GET https://app-api.coinify.com/partners/:partner-id/approximate-quote
Endpoints returns the approximate quote. If transfer media are provided (transferInMedium
and transferOutMedium
) transfer fees are also used for calculating the quote amount.
See request query and response example with additional info about the parameters on the right.
See trade quote for details and explanation of how to create an actual quote you can use to create a trade.
Query params
Parameter | Description |
---|---|
baseAmount |
Amount to get a price quote for. Denominated in baseCurrency . |
baseCurrency |
Currency to get a quote for. |
quoteCurrency |
Currency to get the quote in. |
transferInMedium |
(optional) Transfer in medium see payment methods. |
transferOutMedium |
(optional) Transfer out medium see payment methods. |
See more details about the approximate quote response parameters in the trade price quote section.
Trader KYC
Example of JWT payload
{
"traderId": 123,
"externalId": "id-of-the-partner-kyc-review"
}
Example of a request
{
"traderId": 123,
"externalId": "id-of-the-partner-kyc-review",
"state": "completed",
"approveTime": "2018-11-19T01:23:35Z",
"createTime": "2018-11-19T00:23:35Z",
"pep": {
"approved": true,
"approveTime": "2018-11-19T01:23:35Z"
},
"sanction": {
"approved": true,
"approveTime": "2018-11-19T01:23:35Z"
},
"profile": {
"citizenCountry": "DK",
"name": "John doe",
"dateOfBirth": "1988-12-14",
"address": {
"street": "London Street 123",
"city": "London",
"postalCode": "2001",
"country": "UK"
}
}
}
Example of a response
{
"id": "6b54f095-db25-4507-9912-0461fe70768f",
"externalId": "id-of-the-partner-kyc-review"
}
POST https://app-api.coinify.com/partners/:partner-id/kyc
This endpoint will be used by partners to send us KYC review information for traders who are KYC approved by the partner.
Please contact Coinify if you want to use this endpoint.
Authentication
To authenticate request we will use public / private key from trusted email validation.
JWT payload will be different, see example to the right
The JWT token should be sent as a Bearer token in the Authorization header like this
Authorization: Bearer <jwt_token>
Request params
Example of JWT payload
{
"traderId": 123,
"externalId": "id-of-the-partner-kyc-review"
}
Parameter | Type | Default | Description |
---|---|---|---|
traderId |
Integer | Required | ID of the trader |
externalId |
String | Required | Identifier for this KYC review |
state |
String | Required | State of the review, only completed so far |
approveTime |
ISO 8601 | Required | Timestamp of approval |
createTime |
ISO 8601 | Required | Timestamp of creation |
pep |
Object | Required | Object containing PEP details (politically exposed person) |
pep.approved |
Boolean | Required | If profile was pep checked |
pep.approveTime |
ISO 8601 | Required | Timestamp of approval |
sanction |
Object | Required | Object containing sanction check details |
sanction.approved |
Boolean | Required | If profile was sanction checked |
sanction.approveTime |
ISO 8601 | Required | Timestamp of approval |
profile |
Object | Required | Containing profile information |
profile.citizenCountry |
ISO 3166 alpha-2 | Required | Citizen country code |
profile.name |
String | Required | Full name of the trader |
profile.dateOfBirth |
String | Required | Date of birth (YYYY-MM-DD) |
profile.address |
Object | Required | Object containing address details |
profile.address.street |
String | Required | Street with number |
profile.address.city |
String | Required | City |
profile.address.state |
String | Required | State |
profile.address.postalCode |
String | Required | Postal code |
profile.address.country |
ISO 3166 alpha-2 | Required | Country of residence |
Response
If the response status code is 500
or larger, the partner should resend request using some exponential backoff strategy.
HTTP Response code | JSON data |
---|---|
200 OK |
Success, review already exists (nothing changed). |
201 Created |
Success. |
400 Bad request |
Error interpreting the request. |
401 Unauthorized |
Error, authentication failed. |
A 200
or 201
response object contains the following fields:
Key | Type | Description |
---|---|---|
id |
String (UUID) | Unique identifier for the KYC review. Use this id to upload supporting documents |
externalId |
String | The externalId provided in the request |
Upload supporting documents
Example of JWT payload
{
"reviewId": "6b54f095-db25-4507-9912-0461fe70768f",
"externalId": "your-reference-for-this-document"
}
Raw HTTP Request example
POST /partners/:partner-id/kyc/:review-id/documents HTTP/1.1
Host: app-api.coinify.com
Authorization: Bearer <jwt_token>
Content-Type: image/jpeg
Content-Length: 284
X-Upload-Filename: my_filename.jpg
X-Upload-Document-Type: national_id
X-Upload-External-Id: your-reference-for-this-document
<<raw image content>>
Example response
{
"id": "3ebb9ba1-015e-4a71-816d-56f57f711f4c",
"externalId": "your-reference-for-this-document"
}
POST https://app-api.coinify.com/partners/:partner-id/kyc/:review-id/documents
You can upload supporting documents to an existing KYC review by making a POST
request to
this endpoint, where :review-id
is the id
returned from the
POST /partners/:partner-id/kyc
endpoint.
The body of the request must contain the raw document data
Request headers
All of the HTTP headers in below table are required. Failure to provide either will result in an error
Name | Description |
---|---|
Authorization |
Must contain Bearer <jwt_token> |
Content-Type |
MIME type of supporting document |
X-Upload-External-Id |
Your (partner’s) reference for this document. This must match the externalId value in the supplied <jwt_token> . |
X-Upload-Document-Type |
Document type. Must be one of the following values: passport , national_id , or drivers_license accompanied by a selfie . |
X-Upload-Filename |
Original filename of document |
JWT payload
The JWT Bearer
token supplied in the Authorization
header must contain the following data: (see example to the right)
Key | Type | Description |
---|---|---|
reviewId |
String (UUID) | Identifier for the review to upload document to. Must match the :review-id path parameter. |
externalId |
String | Unique identifier for the supporting document. Must match the X-Upload-External-Id header value provided in the request |
Response
If the response status code is 500
or larger, the partner should resend request using some exponential backoff strategy.
HTTP Response code | JSON data |
---|---|
201 Created |
Success |
400 Bad request |
Error interpreting the request. |
401 Unauthorized |
Error, authentication failed. |
A 200
or 201
response object contains the following fields:
Key | Type | Description |
---|---|---|
id |
String (UUID) | Unique identifier for the supporting document. |
externalId |
String | The X-Upload-External-Id header value provided in the request |
Supported countries
Example of the response
{
"AT": {
"supported": true,
"supportedTransferInMedia": [
"card"
]
},
"BE": {
"supported": true,
"supportedTransferInMedia": [
"card",
"bank",
"blockchain"
]
},
"BG": {
"supported": true,
"supportedTransferInMedia": [
"card",
"bank",
"blockchain"
]
},
"HR": {
"supported": true,
"supportedTransferInMedia": [
"card",
"bank",
"blockchain"
]
},
...
"US": {
"supported": false,
"states": {
"LA": {
"supported": false,
"supportedTransferInMedia": []
},
"OR": {
"supported": false,
"supportedTransferInMedia": [
"card",
"bank",
"blockchain"
]
},
"CT": {
"supported": false,
"supportedTransferInMedia": [
"card",
"bank",
"blockchain"
]
}
...
}
}
}
GET https://app-api.coinify.com/countries/:id
This endpoint will be used by partners to get the list of supported countries and the transfer media supported in each country/state.
Request params
Parameter | Type | Description |
---|---|---|
id |
UUID | UUID provided to the partner |
Response
HTTP Response code | JSON data |
---|---|
200 OK |
Success, response as shown to the right |
400 Bad request |
Error invalid_request : id is invalid. |
404 Bad request |
Error not_found : id is missing from the request. |
Payment methods
GET https://app-api.coinify.com/partners/:id/payment-methods
This endpoint will be used by partners to get list of supported payment methods and minimum / maximum limits before signing up the trader.
Because we use individual risk assessment limits might be different for the individual trader.
You can find more info on the blockchain network and smart contract address behind each supported cryptocurrency here for:
Request params
Parameter | Type | Description |
---|---|---|
id |
UUID | UUID provided to the partner |
Response
Response is similar to Payment methods endpoint from trader requests, except canTrade
and cannotTradeReasons
because they apply to individual traders.
HTTP Response code | JSON data |
---|---|
200 OK |
Success, response as shown to the right |
400 Bad request |
Error invalid_request : id is invalid. |
404 Bad request |
Error not_found : id is missing from the request. |
User
A user is the entity used for authentication.
Request email verification
Used to request a verification email. Should only be used if the user missed the email sent after registration.
POST /users/me/request-email-verification
Response and request are both empty
HTTP Response code | JSON data |
---|---|
200 OK |
Success, empty body |
Error codes | Description |
---|---|
invalid_request |
Email already verified |
Update password
Example request for PATCH /users/me
{
"oldPassword": "myoldpassword1337",
"newPassword": "mynewpassword1337"
}
Example response for PATCH /users/me
{
"id": 1234,
"email": "user@email.com"
}
PATCH users/me
Update information about a specific user.
Request parameter | Type | Description |
---|---|---|
oldPassword |
String | The old password the user wants to change |
newPassword |
String | The new password the user wants to use |
HTTP Response code | JSON data |
---|---|
200 OK |
Success, user response as shown to the right |
400 Bad request |
Error interpreting the request. |
401 Unauthorized |
Error, access token missing. |
Error codes | Description |
---|---|
wrong_password |
The provided oldPassword doesn’t match the existing password. |
invalid_request |
Error in the request body. |
Verify Email
Example request for POST /users/me/verify-email
{
"code": "123654"
}
POST /users/me/verify-email
Request parameter | Type | Description |
---|---|---|
code |
String | 6 digit code supplied by email |
HTTP Response code | JSON data |
---|---|
200 OK |
Success |
400 Bad request |
Error |
404 Not found |
Error |
Error codes | Description |
---|---|
invalid_verification_code |
Invalid code. |
verification_attempts_exceeded |
Invalid code has been posted more than 5 times. |
Request password reset
Starts the password reset flow for given email.
Example request for POST /users/request-password-reset
{
"email": "user@email.com",
"partnerId": "5f51bfe2-d08a-4b43-9d5c-405fd2f2ede6"
}
POST users/request-password-reset
Update information about a specific user.
Request parameter | Type | Description |
---|---|---|
email |
String | Email of the user for whom the password should change. |
partnerId |
UUID | partnerId for partner associated with the user. |
HTTP Response code | JSON data |
---|---|
200 OK |
Success |
400 Bad request |
Error |
Error codes | Description |
---|---|
invalid_request |
Error in the request body. |
Validate security code
Validate security code sent via email when reset password has been requested.
Example request for POST /users/validate-security-code
{
"email": "test@coinify.com",
"partnerId": "5f51bfe2-d08a-4b43-9d5c-405fd2f2ede6",
"code": "123456"
}
Example response from the above request
{
"id": 1234,
"time": 123559953254,
"token": "secret-token-used-for-reset-password"
}
POST /users/validate-security-code
Validate security code sent by email and provide response used for resetting password (see next step).
Request parameter | Type | Description |
---|---|---|
email |
String | |
partnerId |
UUID | Partner ID. |
code |
String | Security code provided in email. |
HTTP Response code | JSON data |
---|---|
200 OK |
Success |
400 Bad request |
Error |
404 Not found |
Error user or security code is not found. |
Error codes | Description |
---|---|
security_code_not_found |
User or security code is not found. |
invalid_security_code |
Security code is invalid. |
security_code_attempts_exceeded |
Max attempts exceeded. |
Reset password
Sets new password for the user.
Example request for POST /users/reset-password
{
"id": 1,
"token": "1234567890asdfghjk",
"time": 123456789,
"password": "mynewpassword"
}
POST /users/reset-password
Request parameter | Type | Description |
---|---|---|
id |
Integer | ID of the user who will get new password. |
token |
String | Token for checking validity of the request. |
time |
Integer | Time of the request. |
password |
String | New password. |
HTTP Response code | JSON data |
---|---|
200 OK |
Success |
400 Bad request |
Error interpreting the request. |
404 Not found |
Error user is not found. |
Error codes | Description |
---|---|
invalid_password_reset_token |
The provided token is not valid. |
user_not_found |
User with given id does not exist. |
Reset offline token
Example of JWT payload
{
"email": "trader@123.com"
}
Example of a request
{
"email": "trader@123.com",
"partnerId": "5f51bfe2-d08a-4b43-9d5c-405fd2f2ede6"
}
Example of a response
{
"offlineToken": "new-offline-token"
}
Endpoint to reset the offline token for a user. Old one will be invalidated, and a new one will be issued.
POST /users/reset-offline-token
Authentication
To authenticate request we will use public / private key from trusted email validation.
JWT payload will be different. The payload must contain the email equal to the request body email, see example to the right.
The JWT token should be sent as a Bearer token in the Authorization header like this
Authorization: Bearer <jwt_token>
Request parameter | Type | Description |
---|---|---|
email |
String | Email of the user |
partnerId |
UUID | Partner ID |
HTTP Response code | JSON data |
---|---|
200 OK |
Success |
400 Bad request |
Error interpreting the request. |
401 Unauthorized |
Error access not granted. |
404 Not found |
Error user is not found. |
Error codes | Description |
---|---|
missing_argument |
Request param missing. |
user_not_found |
User with given email does not exist. |
invalid_partner_token |
Partner token not provided or invalid. |
Trader
A trader is an entity that can perform trades.
You can create a trader entity with the trader signup endpoint.
Mandatory trader information
We distinguish between two types of trader accounts, individual
and corporate
.
In order for the trader to be able to complete their trades they must provide some mandatory information first.
Please find the mandatory API endpoints for each trader account type below.
Individual trader mandatory endpoints:
- Provide address
- Provide source of funds
- Politically Exposted Person status
- Complete trader identification (KYC)
Corporate trader mandatory endopoints:
- Provide corporate details
- Provide address
- Provide source of funds
- Shareholder relations status
- Politically Exposed Person status
Once the corporate trader provides the above listed information, they will receive a KYB email to provide necessary documents.
Get trader information
Example response for
GET /traders/me
{
"id": 754035,
"email": "trader@example.com",
"personalInformation": {
"name": "Jens Jensen"
},
"corporateDetails": null,
"isEmailVerified": true,
"identificationState": "approved",
"corporateDetailsState": "N/A",
"addressState": "approved",
"sourceOfFundsState": "approved",
"kybState": "N/A",
"shareholderRelationState": "N/A",
"proofOfSourceOfFundsState": "pending",
"enhancedProofOfSourceOfFundsState": "pending",
"pepState": "pep",
"tradeSubscriptionsAllowed": false,
"consentAdditionalMails": true,
"accountType": "individual"
}
GET https://app-api.coinify.com/traders/me
This endpoint returns a trader object for your trader, provided you are authorized as a trader.
Key | Type | Description |
---|---|---|
id |
Integer | ID of the trader |
email |
String | Email address of the trader |
personalInformation |
Object | object containing personal information (null until name of trader is set) |
→name |
string | Name of trader |
corporateDetails |
Object | Object containing corporate details ( will be null until corporate details is provided ) |
→name |
string | name of corporation |
isEmailVerified |
Boolean | Is email verified |
identificationState |
String | State of traders identity verification. Possible states are approved , N/A and pending |
corporateDetailsState |
String | State of traders corporate details. Possible state are approved , N/A and pending |
addressState |
String | State of trader address. Possibles states are approved , pending |
sourceOfFundsState |
String | State of traders source of funds. Possible states are approved , N/A and pending |
kybState |
String | State of kyb process. Possible states are approved , documents_requested , N/A and pending |
enhancedDueDiligenceState |
String | State of trader’s Enhanced Due Diligence (EDD). If the trader is marked as high-risk, Coinify will do the EDD and a questionnaire will be sent to the trader’s email address. Possible states are pending , questionnaire_sent , approved , or rejected |
shareholderRelationState |
String | State of shareholderRelation, Possible states are pending , related , not_related , ´N/A` |
proofOfSourceOfFundsState |
String | State of traders proof of source of funds. If the trader traded for more than 10000 EUR we will sent him an email with proof of source of funds questionaire and he won’t be allowed to trade until it is approved. Possible states are approved , rejected , questionaire_sent and pending |
enhancedProofOfSourceOfFundsState |
String | State of traders enhanced proof of source of funds. Possible states are approved and pending |
pepState |
String | State of traders PEP. Possible states are pep , non_pep , pep_relative , N/A and pending |
consentAdditionalMails |
Boolean | Determines whether the user consents to receive additional email from Coinify. This is to comply with GDPR |
accountType |
String | Determines the trader account type, possible account types are individual and corporate |
Response
HTTP Response code | JSON data |
---|---|
200 OK |
Success, response as shown to the right. |
404 Not found |
Error, trader not found. |
Address
Provide Address
Example request for
POST /traders/me/address
{
"street": "Best street 1",
"city": "City",
"postalCode": "1000"
}
POST https://app-api.coinify.com/traders/me/address
Endpoint to provide address
Request object
The parameters in the request object are as follows:
Parameter | Type | Default | Description |
---|---|---|---|
street |
String | Required | Residential street |
city |
String | Required | Residential city |
postalCode |
String | null |
Residential postal code≤ |
Response
HTTP Response code | JSON data |
---|---|
201 Created |
Success, empty object {} |
400 Bad request |
Error, already provided |
401 Unauthorized |
Error, access token missing. |
403 Forbidden |
Error, invalid access token. |
Get Address
Example response for
GET /traders/me/address
{
"street": "Best street 1",
"city": "City",
"postalCode": "1000",
"country": "US",
"state": "NY"
}
GET https://app-api.coinify.com/traders/me/address
Endpoint to get address
Response
HTTP Response code | JSON data |
---|---|
200 OK |
Success |
401 Unauthorized |
Error, access token missing. |
403 Forbidden |
Error, invalid access token. |
Trader Identification
In order to comply with financial regulations, all traders must undergo a KYC (Know-Your-Customer) review before they can start trading. A KYC review typically revolves around verifying the identity of the trader.
This section deals with the mechanisms for doing so.
Identification attempt object
Example of an identification attempt:
{
"id": "0cf12488-0dcf-4f99-96c4-e1ec879c906d",
"state": "approved",
"createTime": "2019-11-26T07:45:44.852Z",
"approveTime": "2019-11-26T07:49:19.126Z",
"expireTime": "2028-03-04T00:00:00.000Z",
"redirectUrl": "https://www.example.com/redirect-client-to-this-url",
"sdkToken": null,
"returnUrl": null
}
An identification attempt is represented as an object as seen to the right. The table below describes each of the properties in the object.
Key | Type | Description |
---|---|---|
id |
String | Identifier (UUID) for the identification attempt |
state |
Identification attempt state | State of the identification attempt |
createTime |
ISO 8601 time | Timestamp for when this identification attempt was first initiated. |
approveTime |
ISO 8601 time or null |
Timestamp for when this identification attempt was approved |
expireTime |
ISO 8601 time or null |
Timestamp for when this identification attempt will expire |
redirectUrl |
URL (String) | URL to redirect the user to in order to perform the identification |
sdkToken |
JWT (String) | Deprecated The parameter is till returned in the response but will always be null |
returnUrl |
URL (String) or null |
URL to return to when identification attempt is submitted or failed. Returning after identification flow below for more information. |
rejectReason |
Identification attempt rejection reason or null |
Machine-readable reason for rejecting this identification attempt, if state is 'rejected' . |
Identification attempt state
An identification attempt can be in a number of well-defined states throughout its lifetime.
State | Description |
---|---|
pending |
(Starting state) Identification attempt has been initiated, but is waiting for action from the end-user. |
processing |
(Intermediate state) Identification attempt is currently being processed. |
approved |
(Final state) Identification attempt was successfully approved. (This state is also used if the identification attempt was manual and accepted) |
expired |
(Final state) Identification attempt expired because the documents used to identify with has expired |
rejected |
(Final state) Identification attempt was rejected |
Identification attempt rejection reasons
If an identification attempt state is rejected
, the identification attempt contains a rejectReason
providing a reason
for the rejection. The below table lists all currently possible reasons:
Rejection reason | Description |
---|---|
DENIED |
Denied without any further reason |
DENIED_UNSUPPORTED_ID_TYPE |
The used identification was not supported |
DENIED_UNSUPPORTED_ID_COUNTRY |
The used identification was issued in a country not supported by Coinify |
DENIED_MISSING_DATE_OF_BIRTH |
The used identification didn’t contain a date of birth |
DENIED_YOUNGER_THAN_18_YEARS |
The identified person must be 18 years or older |
DENIED_EXPIRED_IDENTIFICATION |
The used identification has expired |
DENIED_FAILED_IDENTITY_VERIFICATION |
We were unable to ensure that the person submitting the identification was the same person in the ID photo |
ERROR_NOT_READABLE_ID.PHOTOCOPY_BLACK_WHITE |
|
ERROR_NOT_READABLE_ID.PHOTOCOPY_COLOR |
|
ERROR_NOT_READABLE_ID.DIGITAL_COPY |
|
ERROR_NOT_READABLE_ID.NOT_READABLE_DOCUMENT .BLURRED |
|
ERROR_NOT_READABLE_ID.NOT_READABLE_DOCUMENT .BAD_QUALITY |
|
ERROR_NOT_READABLE_ID.NOT_READABLE_DOCUMENT .MISSING_PART_DOCUMENT |
|
ERROR_NOT_READABLE_ID.NOT_READABLE_DOCUMENT .HIDDEN_PART_DOCUMENT |
|
ERROR_NOT_READABLE_ID.NOT_READABLE_DOCUMENT .DAMAGED_DOCUMENT |
|
ERROR_NOT_READABLE_ID.NO_DOCUMENT |
|
ERROR_NOT_READABLE_ID.SAMPLE_DOCUMENT |
|
ERROR_NOT_READABLE_ID.MISSING_BACK |
|
ERROR_NOT_READABLE_ID.WRONG_DOCUMENT_PAGE |
|
ERROR_NOT_READABLE_ID.MISSING_SIGNATURE |
|
ERROR_NOT_READABLE_ID.CAMERA_BLACK_WHITE |
|
ERROR_NOT_READABLE_ID.DIFFERENT_PERSONS_SHOWN |
|
ERROR_NOT_READABLE_ID.MANUAL_REJECTION |
|
MANUAL_REVIEW |
The identification attempt is under manual review by Coinify compliance. This process can last betwen 1-3 days. Coinify will communicate the final result with the trader via email and a callback will be sent to your webhook accordingly. As an API integrated Partner you should inform the user about this accordingly. |
NO_ID_UPLOADED |
No identification was successfully submitted. |
Initiate identification attempt
Example request to
POST /kyc/identification-attempts
{
"returnUrl": "https://mypage.com/ida_complete"
}
Example response from
POST /kyc/identification-attempts
{
"id": "0cf12488-0dcf-4f99-96c4-e1ec879c906d",
"state": "pending",
"createTime": "2019-11-26T07:45:44.852Z",
"approveTime": null,
"expireTime": null,
"redirectUrl": "https://www.example.com/redirect-client-to-this-url",
"returnUrl": "https://mypage.com/ida_complete",
"sdkToken": null
}
POST https://app-api.coinify.com/kyc/identification-attempts
Initiates an identification attempt. The user must be redirected to the URL specified in the redirectUrl
parameter of the response in order to actually identify themselves.
Request object
The parameters in the request object are as follows:
Parameter | Type | Default | Description |
---|---|---|---|
returnUrl |
URL (String) | No value | URL to return to after the process is done when using the redirectUrl . See Returning after identification flow below for more information. |
deviceType |
String | No value | Type of device on which the user will perform identification. Used to provide the best possible user experience. Possible values are android (Android phone/tablet), ios (iPhone/iPad), desktop (Non-mobile device). If left blank, auto-detection will be attempted from the User-Agent HTTP header of the request. |
Response
HTTP Response code | JSON data |
---|---|
201 Created |
Success, Identification attempt object as shown to the right. |
400 Bad request |
Error interpreting the request. |
401 Unauthorized |
Error, access token missing. |
403 Forbidden |
Error, invalid access token. |
Returning after identification flow
If a returnUrl
is specified during the initiate identification attempt API call, then the user will be redirected to that URL once they have submitted (or failed to submit) their identification.
The returnUrl
is augmented with a result
query parameter containing either a 'submitted'
or 'failed'
value. Examples:
returnUrl |
Result of flow | Actual URL that user is redirected to |
---|---|---|
https://mypage.com/ida_return |
Documents submitted | https://mypage.com/ida_return?result=submitted |
https://mypage.com/ida_return |
Failed | https://mypage.com/ida_return?result=failed |
https://mypage.com/ida_return?other_query_parameter=1234 |
Documents submitted | https://mypage.com/ida_return?other_query_parameter=1234&result=submitted |
https://mypage.com/ida_return?other_query_parameter=1234 |
Failed | https://mypage.com/ida_return?other_query_parameter=1234&result=failed |
Get identification attempt
Example request for
GET /kyc/identification-attempts/0cf12488-0dcf-4f99-96c4-e1ec879c906d
{
"id": "0cf12488-0dcf-4f99-96c4-e1ec879c906d",
"state": "approved",
"createTime": "2019-11-26T07:45:44.852Z",
"approveTime": "2019-11-26T07:49:19.126Z",
"expireTime": "2028-03-04T00:00:00.000Z",
"redirectUrl": "https://www.example.com/redirect-client-to-this-url",
"sdkToken": null,
"returnUrl": null
}
GET https://app-api.coinify.com/kyc/identification-attempts/<id>
This endpoint allows you to query a specific identification attempt by ID.
Response
HTTP Response code | JSON data |
---|---|
200 OK |
Success, Identification attempt object as shown to the right. |
401 Unauthorized |
Error, access token missing. |
403 Forbidden |
Error, invalid access token. |
404 Not Found |
Error retrieving identification attempt with the specified ID. |
List identification attempts
Example request for
GET /kyc/identification-attempts
[
{
"id": "0cf12488-0dcf-4f99-96c4-e1ec879c906d",
"state": "approved",
"createTime": "2019-11-26T07:45:44.852Z",
"approveTime": "2019-11-26T07:49:19.126Z",
"expireTime": "2028-03-04T00:00:00.000Z",
"redirectUrl": "https://www.example.com/redirect-client-to-this-url",
"sdkToken": null,
"returnUrl": null
},
{
// next attempt...
}
]
GET https://app-api.coinify.com/kyc/identification-attempts
This endpoint allows you to get all identification attempts for the trader.
The identification attempts are ordered by createTime
(newest first).
Response
HTTP Response code | JSON data |
---|---|
200 OK |
Success, A list of identification attempt objects as shown to the right. |
401 Unauthorized |
Error, access token missing. |
403 Forbidden |
Error, invalid access token. |
Skip identification process (KYC) - Sandbox environment only
For testing purposes, you can skip the identification process in Sandbox environment. You can find a short article on how to do this by following this link.
Corporate Details
Provide Corporate Details
Example request for
POST /traders/me/corporate-details
{
"name": "Some corporation name",
"industry": "wholesale_and_retail_trade",
"website": "www.somecorporation.com",
"description": "some corporation description"
}
POST https://app-api.coinify.com/traders/me/corporate-details
Endpoint to provide corporate details
Request object
The parameters in the request object are as follows:
Parameter | Type | Default | Description |
---|---|---|---|
name |
String | Required | Name of the corporation |
industry |
String | Required | Can be one of these values 'wholesale_and_retail_trade', 'accommodation_and_food_service', 'information_and_communication', 'professional_or_scientific_and_technical_activities', 'administrative_and_support_service_activities', 'education', 'financial_and_insurance_activities', 'human_health', 'real_estate_activities', 'arts_or_entertainment_or_recreation', 'non_profit_and_public_sectors', 'other' |
otherIndustry |
String | null |
Alternative industry, needs to be supplied if 'other' is chosen as industry |
website |
String | null |
Corporate website |
description |
String | Required | Description of the corporation |
Response
HTTP Response code | JSON data |
---|---|
201 Created |
Success, empty object {} |
400 Bad request |
Error, already provided |
401 Unauthorized |
Error, access token missing. |
403 Forbidden |
Error, invalid access token. |
Source of funds
Get annual trade volume buckets
Example response for
GET /traders/me/source-of-funds/annual-trade-volume-buckets
[
{
"id": 1,
"lt": 10000,
"currency": "EUR"
},
{
"id": 2,
"lt": 50000,
"gte": 10000,
"currency": "EUR"
},
{
"id": 3,
"gte": 50000,
"currency": "EUR"
}
]
GET https://app-api.coinify.com/traders/me/source-of-funds/annual-trade-volume-buckets
Endpoint to get annual trade volume buckets. ID of the response will be used for next request
Provide source of funds
Example request for
POST /traders/me/source-of-funds
{
"originOfFunds": "crypto-mining",
"annualTradeVolumeBucketId": 1,
"areFundsFromLegalActivities": true
}
POST https://app-api.coinify.com/traders/me/source-of-funds
Endpoint to provide source of funds.
Request object
The parameters in the request object are as follows:
Parameter | Type | Default | Description |
---|---|---|---|
originOfFunds |
String | Required | Must be one of the following values for an individual trader: 'occupation','inheritance','investment-proceeds','crypto-mining','savings', 'loan', 'crypto-purchased-elsewhere', 'sale-of-property', 'sale-of-precious-goods' . Options for a corporate trader: 'investment-proceeds', 'crypto-mining', 'business-income' . |
annualTradeVolumeBucketId |
Integer | Required | Id of bucket see get annual trade volume buckets endpoint to get ID. |
areFundsFromLegalActivities |
Boolean | Required | Must be set to true for a valid request |
Response
HTTP Response code | JSON data |
---|---|
201 Created |
Success, empty object {} |
400 Bad request |
Error, already provided |
401 Unauthorized |
Error, access token missing. |
403 Forbidden |
Error, invalid access token. |
Shareholder Relations
Provide Shareholder Relation
Example request for
POST /traders/me/shareholder-relation
{
"shareholderRelationState": "not_related"
}
POST https://app-api.coinify.com/traders/me/shareholder-relation
Endpoint to provide shareholder relation state. Its only applicable for corporate traders.
Used for providing whether shareholders in the corporations are related in some way
Request object
The parameters in the request object are as follows:
Parameter | Type | Default | Description |
---|---|---|---|
shareholderRelationState |
String | Required | Shareholder Relation state can be one of the following: related , not_related |
Response
HTTP Response code | JSON data |
---|---|
200 OK |
Success |
400 Bad request |
Error, invalid request |
401 Unauthorized |
Error, access token missing. |
403 Forbidden |
Error, invalid access token. |
Politically Exposed Person
Provide PEP (Politically Exposed Person)
Example request for
POST /traders/me/pep
{
"pepState": "pep"
}
POST https://app-api.coinify.com/traders/me/pep
Endpoint to provide PEP state.
Request object
The parameters in the request object are as follows:
Parameter | Type | Default | Description |
---|---|---|---|
pepState |
String | Required | PEP can be one of these values for individual traders 'pep','non_pep','pep_relative' and these values for corporate traders 'pep','non_pep' |
Response
HTTP Response code | JSON data |
---|---|
200 OK |
Success |
400 Bad request |
Error, invalid request |
401 Unauthorized |
Error, access token missing. |
403 Forbidden |
Error, invalid access token. |
Resend emails
The following endpoints allow you to resend specific emails that are usually sent to the traders by our system, in case for some reason the trader does not receive the emails in the first place or does not provide the necessary information in time.
Resend Proof of Source of Funds (PSOF) Email
POST https://app-api.coinify.com/traders/me/resend-proof-of-source-of-funds-email
This endpoint enables you to resend a PSOF email to the trader. A PSOF email is usually sent when a trader reaches a significant volume of trades.
If the trader does not complete the PSOF before it expires, you can resend it with a request to this endpoint.
You must be authenticated as the specific trader to use this endpoint and for the email to be delivered to the correct address.
HTTP Response code | JSON data |
---|---|
200 OK |
Success |
400 Bad request |
Error, invalid request |
401 Unauthorized |
Error, access token missing. |
403 Forbidden |
Error, invalid access token. |
Resend Enhanced Due Diligence (EDD) Email
POST https://app-api.coinify.com/traders/me/resend-enhanced-due-diligence-email
This endpoint enables you to resend an EDD email to the trader. An EDD email is usually sent when the customer is marked as a high-risk trader.
If the trader does not complete the EDD before it expires, you can resend it with a request to this endpoint.
You must be authenticated as the specific trader to use this endpoint and for the email to be delivered to the correct address.
HTTP Response code | JSON data |
---|---|
200 OK |
Success |
400 Bad request |
Error, invalid request |
401 Unauthorized |
Error, access token missing. |
403 Forbidden |
Error, invalid access token. |
Resend KYB upload email (Corporate traders only)
POST https://app-api.coinify.com/traders/me/resend-kyb-upload-email
This endpoint enables you to resend the KYB document upload email to a corporate trader.
The KYB email is automatically sent when a new corporate trader creates an account and provides the basic mandatory information required from all corporate traders. See mandatory info that a corporate trader needs to provide here.
Trader authentication is required to use this endpoint and for the email to be delivered to the correct address.
HTTP Response code | JSON data |
---|---|
200 OK |
Success |
400 Bad request |
Error, invalid request |
401 Unauthorized |
Error, access token missing. |
403 Forbidden |
Error, invalid access token. |
Trades
This section describes the trade object as well as the endpoints that allow you to create, query and manipulate trades:
- See all payment methods for trades
- Request trade price quote
- Get price quote details
- See payment methods for price quote
- Create trade
- Get trade information
- Cancel trade
- Update trade’s price quote
The following sections describe the trade object and continue to describe each of the endpoints in greater detail.
Trade object
Example
awaiting_transfer_in
trade object buying 2.41526674 BTC for 1,000.00 USD
{
"id": 113475347,
"traderId": 754035,
"traderEmail": "customer@coinify.com",
"state": "awaiting_transfer_in",
"inCurrency": "USD",
"outCurrency": "BTC",
"inAmount": 1000.00,
"outAmountExpected": 2.41526674,
"transferIn": {
"currency": "USD",
"sendAmount": 1000.00,
"receiveAmount": 1000.00,
"medium": "card",
"details": {
"redirectUrl": "https://provider.com/payment/d3aab081-7c5b-4ddb-b28b-c82cc8642a18"
}
},
"transferOut": {
"currency": "BTC",
"medium": "blockchain",
"sendAmount": 2.41526674,
"receiveAmount": 2.41526674,
"details": {
"account": "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa"
}
},
"quoteExpireTime": "2016-04-01T12:38:19Z",
"updateTime": "2016-04-01T12:27:36Z",
"createTime": "2016-04-01T12:23:19Z"
}
Example
completed
trade object buying 2.41551728 BTC for 1,000.00 USD
{
"id": 113475347,
"traderId": 754035,
"traderEmail": "customer@coinify.com",
"state": "completed",
"inCurrency": "USD",
"outCurrency": "BTC",
"inAmount": 1000.00,
"outAmount": 2.41551728,
"transferIn": {
"currency": "USD",
"sendAmount": 1000.00,
"receiveAmount": 1000.00,
"medium": "card",
"details": {
"redirectUrl": "https://provider.com/payment/d3aab081-7c5b-4ddb-b28b-c82cc8642a18"
}
},
"transferOut": {
"currency": "BTC",
"medium": "blockchain",
"sendAmount": 2.41526674,
"receiveAmount": 2.41526674,
"details": {
// Trader's bitcoin address that has received the 2.41551728 BTC
"account": "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa",
// The BTC transaction that sent out the BTC to the above address
"transaction": "000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f"
}
},
"updateTime": "2016-04-01T12:27:36Z",
"createTime": "2016-04-01T12:23:19Z"
}
Example
completed
trade object selling 2.41551728 BTC for 1,000.00 USD
{
"id": 113475348,
"traderId": 754035,
"traderEmail": "customer@coinify.com",
"state": "completed",
"inCurrency": "BTC",
"outCurrency": "USD",
"inAmount": 2.41551728,
"outAmount": 1000.00,
"transferIn": {
"currency": "BTC",
"sendAmount": 2.41551728,
"receiveAmount": 2.41551728,
"medium": "blockchain",
"details": {
// Coinify's bitcoin address to where the trader sent the 2.41551728 BTC
"account": "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa",
"paymentUri": "bitcoin:1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa?amount=2.41551728",
// The BTC transaction that sent out the BTC to the above address
"transaction": "000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f"
}
},
"transferOut": {
"currency": "USD",
"sendAmount": 1000.00,
"receiveAmount": 1000.00,
"medium": "bank",
"mediumReceiveAccountId": 12345 // Reference to the traders bank account
},
"updateTime": "2016-04-01T12:27:36Z",
"createTime": "2016-04-01T12:23:19Z"
}
The following table defines the fields of a trade object:
Key | Type | Description |
---|---|---|
id |
Integer | Unique ID for this trade |
traderId |
Integer | Reference to the trader that created the trade |
traderEmail |
String | Email address of trader that created the trade |
state |
Trade state | The current state of the trade |
inCurrency |
String | Currency (ISO 4217) denominating inAmount . |
outCurrency |
String | Currency (ISO 4217) denominating outAmount or outAmountExpected . |
inAmount |
Float | The amount of inAmount that this trade covers. Is always positive. |
outAmount |
Float | (Optional) The amount of outCurrency that this trade resulted in. Is always positive. NOTE: This field is only defined if the state of the trade is completed . For all other states, see the outAmountExpected field. |
outAmountExpected |
Float | The amount of outCurrency that this trade is expected to result in. Is always positive. |
transferIn |
Transfer object | Object describing how we (Coinify) will receive/have received the money to fund the trade. |
transferOut |
Transfer object | Object describing how we (Coinify) will send/have sent the result of the trade back to the trader. |
quoteExpireTime |
ISO 8601 time | (Optional) The time when the price quote underlying this trade expires. NOTE: This field is only defined in the following states: awaiting_transfer_in , processing , reviewing , payment_authorized . |
updateTime |
ISO 8601 time | The time when the trade was last updated. |
createTime |
ISO 8601 time | Timestamp for when this trade was first created |
Trade states
The following table lists all possible states of a trade (the state
property):
State | Description |
---|---|
awaiting_transfer_in |
(Starting state) Trade was successfully created, and is waiting for trader’s payment |
payment_authorized |
(Only if transferIn.medium = ‘card’) We have reserved trade.inAmount on customers card. Followed immediately by processing if trader has performed KYC (completed identity verification, provided personal information, and provided source of funds), and the quote is not expired. |
processing |
Trade has received the trader’s payment, and we are processing the trade. |
completed |
(Ending state) Trade completed successfully. |
cancelled |
(Ending state) Trade cancelled. |
rejected |
(Ending state) Trade was rejected. |
expired |
(Ending state) Trade expired before it completed. |
The states, as well as the possible transitions between them, are visualized in this diagram:
Transfer object
Each trade has two transfer objects - transferIn
and transferOut
- which contain details about how Coinify will receive money from the trader, and how Coinify will send money back to the trader, respectively.
Each transfer object contains the following keys:
Key | Type | Description |
---|---|---|
currency |
String | Currency (ISO 4217) that this transfer is/will be denominated in. |
sendAmount |
Float | Amount that is/will be sent to this transfer. Denominated in currency . Is always positive. |
receiveAmount |
Float | Amount that this transfer will result in. Denominated in currency . Is always positive and equal to or smaller then sendAmount . The difference between the sendAmount and the receiveAmount will be the fee of the transfer. |
medium |
String | Transfer medium. The medium transferring the currency. |
mediumReceiveAccountId |
Integer | Reference to our internal accounts. Note at the moment we only have internal bank accounts. |
details |
Medium details | Information relevant for this medium |
Transfer media
Medium | Description |
---|---|
blockchain |
Blockchain - Transfer on a public blockchain, such as Bitcoin. NOTE: The currency field of the transfer object will determine which blockchain currency. |
card |
Card - Card payment. |
bank |
Bank - Bank account payment. If transfer is incoming we sent our own bank details as the details object. And if transfer is outgoing its the details of the traders bank account. |
Blockchain
Details object
Key | Type | Description |
---|---|---|
account |
String | Account receiving the money in this transfer (blockchain address). |
accountSignature |
String | HMAC-SHA-256 signature signing the account value. Only defined if a shared secret has been established with Coinify. |
memo |
String | Optional memo for currencies where memo/destination tag is supported. |
transaction |
String | Transaction sending the money in this transfer. Only defined if money has been sent. |
refundAccount |
String | Account to refund the money in this transfer in case of failure. Only defined if currency != 'BTC' . |
refundAccountSignature |
String | HMAC-SHA-256 signature signing the refundAccount value. Only defined if refundAccount is defined and a shared secret has been established with Coinify. |
paymentUri |
String | Payment URI scheme to generate QR code. |
Card
Details object
Below, a description of the request/response objects for creating a trade.
Request object
Key | Type | Description |
---|---|---|
returnUrl |
String | The return URL to which the user to be sent back after the payment has been created. Can be provided when creating a trade. |
Reponse object
Key | Type | Description |
---|---|---|
redirectUrl |
String | Source URL to process the payment. |
returnUrl |
String | The return URL to which the user to be sent back after the payment has been created. |
Bank
Details object
Key | Type | Description |
---|---|---|
referenceText |
String | Text that the bank transfer must contain in order for Coinify to correctly register what trade it concerns. |
account |
Object | Object with additional information about the bank account. |
→currency |
String | Currency of the bank account. |
→type |
Bank account type | Type of the bank account |
→bic |
String | For sepa and international its the SWIFT / BIC number and for danish accounts its the REG number. Optional in case of domestic bank tansfers. |
→number |
String | For sepa and international it’s the IBAN (International Bank Account Number). For danish accounts, it’s the BBAN (Basic Bank Account Number). Optional in case of domestic bank transfers. |
→domesticAccountNumber |
String | Account Number for domestic transfers. (Optional) |
→regNo |
String | Reg. No. used for some domestic transfers. (Optional) |
→routingCode |
String | Routing Code used for some domestic transfers. (Optional) |
→sortCode |
String | Sort Code used for some domestic transfers. (Optional) |
→bsb |
String | BSB used for some domestic transfers. (Optional) |
bank |
Object | Object with additional information about the bank. |
→name |
String | Name of the bank. |
→address |
Object | Object with information about the address of the bank. |
→street |
String | Street address. |
→zipcode |
String | Zip/Postal code. |
→city |
String | City. |
→state |
String | State. |
→country |
String | ISO 3166-1 alpha-2 country code |
holder |
Object | Object with additional information about the bank account holder. |
→name |
String | Name of the bank account holder. |
→address |
Object | Object with information about the address of the account holder. |
→street |
String | Street address. |
→zipcode |
String | Zip/Postal code. |
→city |
String | City. |
→state |
String | State. |
→country |
String | ISO 3166-1 alpha-2 country code |
Payment methods
This section contains information about different methods to buy/sell blockchain currencies.
The currently available payment methods are:
- Buy crypto currency with bank transfer - (in) bank, (out) blockchain
- Buy crypto currency with card transfer - (in) card, (out) blockchain
- Sell bitcoins to bank transfer - (in) blockchain, (out) bank
Payment method object
The object represents a single payment method, containing the information about how and with what currencies the money transfer can and should be performed, and optionally the fees that are going to be applied to the amounts.
Key | Type | Description |
---|---|---|
inMedium |
String | The medium for the in transfer of the trade - by what way the customer pays |
outMedium |
String | The medium for the out transfer of the trade - what way the customer receives funds |
inCurrencies |
List | The possible currencies in which the incoming amount can be made, optionally filtered by request arguments |
outCurrencies |
List | The possible currencies in which the outgoing amount can be made, optionally filtered by request arguments |
inFixedFees |
Object | Object of inCurrencies and fixed fees for each currency for the in transfer. |
inPercentageFee |
Float | Percentage fee for the in transfer. |
minimumInFees |
Object | Minimum fee for the in transfer. |
outFixedFees |
Object | Object of outCurrencies and fixed fees for each currency for the out transfer. |
outPercentageFee |
Float | Percentage fee for the out transfer. |
minimumOutFees |
Object | Minimum fee for the out transfer. |
minimumInAmounts |
Object | Object of inCurrencies and the minimum limit for each. |
limitInAmounts |
Object | Object of inCurrencies and the trader’s current limit for each currency, based on the current limits of the trader. Note: Only included for authenticated requests. |
canTrade |
Boolean | Can this trader create new trades for this payment method? Note: Only included for authenticated requests. If inCurrency , inAmount , outCurrency and outAmount are all provided in request, this value determines if a trade with the specific amounts/currencies can be made. Otherwise, this value determines if any trade can be made with this payment method. |
cannotTradeReasons |
List | (Optional) List of reason objects why the trader cannot create new trades (why canTrade is false ). Note: Only included for authenticated requests if canTrade is false . |
inCurrency |
String | (Optional, if inCurrency parameter provided) Echo of inCurrency parameter. |
inAmount |
Float | (Optional, if inAmount parameter provided) Echo of inAmount parameter. |
outCurrency |
String | (Optional, if outCurrency parameter provided) Echo of outCurrency parameter. |
outAmount |
Float | (Optional, if outAmount parameter provided) Echo of outAmount parameter. |
cannotTradeReason
object
This object represents a reason for why a trader cannot create a trade with a given payment method. The only field that is always present in the object is the reasonCode
. Depending on the value of the reasonCode
, other reason-specific fields may be present in the object.
Key | Type | Description |
---|---|---|
reasonCode |
String | Machine-readable code for the reason why the trade cannot create a trade. See Possible reasonCode s for a list of possible values |
Possible reasonCode
s
reasonCode value |
Description | Extra fields |
---|---|---|
forced_delay |
Trader must wait until a specific time before creating trade | delayEnd (string): ISO-8601 timestamp for when the delay is over. |
trade_in_progress |
Trader must wait until a specific trade has completed | tradeId (integer): ID of trade that must be completed |
limits_exceeded |
Creating trade would exceed the trader’s limits | None |
country_not_supported |
Trading is not currently supported in the trader’s country. See supported countries for querying a partner’s supported countries. | None |
List payment methods
Example response for
GET /trades/payment-methods
(authenticated)
[
{
"inMedium": "bank",
"outMedium": "blockchain",
"inCurrencies": ["DKK", "EUR", "USD", "GBP", "CHF"],
"outCurrencies": ["BTC", "ETH", "BSV", "BCH", "XLM", "DASH", "NANO"],
"inFixedFees": {
"DKK": 0,
"EUR": 0,
"USD": 0,
"GBP": 0,
"CHF": 0
},
"inPercentageFee": 0.75,
"minimumInFees": {
"DKK": 49.00,
"EUR": 4.99,
"USD": 4.99,
"GBP": 4.99,
"CHF": 4.99
},
"outFixedFees": {
"BTC": 0.0002,
"ETH": 0,
"BSV": 0,
"BCH": 0,
"XLM": 0,
"DASH": 0,
"NANO": 0
},
"outPercentageFee": 0,
"minimumInAmounts": {
"DKK": 933.38,
"EUR": 125,
"USD": 140.17,
"GBP": 111.35,
"CHF": 140.08
},
"limitInAmounts": {
"DKK": 7500.86,
"EUR": 1000.00,
"USD": 1200.50,
"GBP": 8000.00,
"CHF": 800.00
},
"canTrade": true
},
{
"inMedium": "blockchain",
"outMedium": "bank",
"inCurrencies": ["BTC", "ETH", "BSV", "BCH", "XLM", "DASH", "NANO"],
"outCurrencies": ["DKK", "EUR", "USD", "GBP", "CHF"],
"inFixedFees": {
"BTC": 0,
"ETH": 0,
"BSV": 0,
"BCH": 0,
"XLM": 0,
"DASH": 0,
"NANO": 0
},
"inPercentageFee": 0,
"outFixedFees": {
"DKK": 40.00,
"EUR": 5.40,
"USD": 6.10,
"GBP": 3.70,
"CHF": 4.90
},
"minimumOutFees": {
"DKK": 49.00,
"EUR": 4.99,
"USD": 4.99,
"GBP": 4.99,
"CHF": 4.99
},
"outPercentageFee": 1,
"minimumInAmounts": {
"BTC": 0.01530498,
"ETH": 0.514718127075,
"BSV": 0.65349743,
"BCH": 0.32362399,
"XLM": 1077.1290499,
"DASH": 1.77,
"NANO": 231.41
},
"limitInAmounts": {
"BTC": 0.1224398,
"ETH": 4.117745016597,
"BSV": 5.2279794,
"BCH": 2.58899193,
"XLM": 8617.0323993,
"DASH": 15.2,
"NANO": 1249.23
},
"canTrade": true
},
{
"inMedium": "card",
"outMedium": "blockchain",
"inCurrencies": ["DKK", "EUR", "USD", "GBP", "CHF"],
"outCurrencies": ["BTC", "ETH", "BSV", "BCH", "XLM", "DASH", "NANO"],
"inFixedFees": {
"DKK": 0,
"EUR": 0,
"USD": 0,
"GBP": 0,
"CHF": 0
},
"inPercentageFee": 3.5,
"minimumInFees": {
"DKK": 49.00,
"EUR": 4.99,
"USD": 4.99,
"GBP": 4.99,
"CHF": 4.99
},
"outFixedFees": {
"BTC": 0.0002,
"ETH": 0,
"BSV": 0,
"BCH": 0,
"XLM": 0,
"DASH": 0,
"NANO": 0
},
"outPercentageFee": 0,
"minimumInAmounts": {
"DKK": 149.34,
"EUR": 20,
"USD": 22.43,
"GBP": 17.82,
"CHF": 22.41
},
"limitInAmounts": {
"DKK": 746.71,
"EUR": 100,
"USD": 112.14,
"GBP": 89.08,
"CHF": 112.06
},
"canTrade": false,
"cannotTradeReasons": [
{
"reasonCode": "forced_delay",
"delayEnd": "2016-04-01T12:27:36Z"
}
]
}
]
GET https://app-api.coinify.com/trades/payment-methods
This endpoint returns a list of payment methods objects, available for creating and paying for trades.
If you want know which network/chain does a particular supported cryptocurrency belong to, please find the list here.
Response
HTTP Response code | Error code | JSON data |
---|---|---|
200 OK |
Success, response as shown to the right. | |
400 Bad request |
invalid_argument |
Error, interpreting the request (wrong input values for query parameters). |
500 Internal error |
internal_error |
Error, an internal error happened. |
Request trade price quote
Example request for
POST /trades/quote
// This request is saying: "I want to buy BTC for 1,000.00 USD. How much BTC will I get?"
{
"baseCurrency": "USD",
"quoteCurrency": "BTC",
"baseAmount": -1000.00
}
Example response for
POST /trades/quote
// This response is saying "We'll give you 2.41551728 BTC for 1,000.00 USD"
{
"id": 123456,
"baseCurrency": "USD",
"quoteCurrency": "BTC",
"baseAmount": -1000.00,
"quoteAmount": 2.41551728,
"issueTime": "2016-04-01T11:47:24Z",
"expiryTime": "2016-04-01T12:02:24Z"
}
POST https://app-api.coinify.com/trades/quote
Before actually creating a trade, you (as a trader) have the option of getting a price quote (i.e. an offer for a specific exchange rate) that is valid for a short period of time. When creating a trade you can reference a price quote and trade using the rate given in the quote.
Request object
The parameters in the request object are as follows:
Parameter | Type | Default | Description |
---|---|---|---|
baseCurrency |
String | Required | Relative to what currency (ISO 4217) do you want a price quote? Denominates baseAmount . |
quoteCurrency |
String | Required | What currency (ISO 4217) do you want the price quote in? |
baseAmount |
Float | Required | Amount to get a price quote for. Denominated in baseCurrency . |
transferIn |
Object | Empty object | An object describing how Coinify will receive the money for this transfer. |
→ medium |
String | null |
Transfer medium |
transferOut |
Object | Empty object | An object describing how Coinify will send the result of the trade to the trader. |
→ medium |
String | null |
Transfer medium |
Request object examples
The following table exemplifies how baseCurrency
and quoteCurrency
should be interpreted.
baseCurrency |
quoteCurrency |
baseAmount |
Interpretation |
---|---|---|---|
"USD" |
"BTC" |
123.45 | I want to buy 123.45 USD for BTC. |
"USD" |
"BTC" |
-123.45 | I want to sell 123.45 USD for BTC. |
"BTC" |
"USD" |
1.5 | I want to buy 1.5 BTC for USD. |
"BTC" |
"USD" |
-1.5 | I want to sell 1.5 BTC for USD. |
Response object
The success response object contains the following fields:
Key | Type | Description |
---|---|---|
id |
Integer | Price quote identifier. Pass this identifier to the create trade endpoint to claim the offer. Note: Not included if anonymous (un-authenticated) request. |
baseCurrency |
String | Currency (ISO 4217) denominating baseAmount , the known amount. |
quoteCurrency |
String | Currency (ISO 4217) denominating quoteAmount , the unknown amount. |
baseAmount |
Float | How much of baseCurrency does this quote apply for? |
quoteAmount |
Float | How much of quoteCurrency is this quote? This is the “result”, the price quote. |
transferIn |
Object | transferIn object, only returned if provided in request |
→ medium |
String | Transfer medium |
→ feeAmount |
Float | Transfer fee (Will be added on the trade) |
→ currency |
String | Currency |
transferOut |
Object | transferOut object, only returned if provided in request |
→ medium |
String | Transfer medium |
→ feeAmount |
Float | Transfer fee (Will be added on the trade) |
→ currency |
String | Currency |
issueTime |
ISO 8601 time | Timestamp for when this price quote was issued. |
expiryTime |
ISO 8601 time | Timestamp for when this price quote expires. |
Response
HTTP Response code | JSON data |
---|---|
201 Created |
Success, A Price quote created and returned object as shown to the right |
401 Unauthorized |
Error, access token missing. |
Get price quote details
Example response (buy) for
GET /trades/quote/:id/details?transferInMedium=card&transferOutMedium=blockchain
{
"transferIn": {
"currency": "EUR",
"isMinimumHandlingFeeApplied": false,
"isMaximumHandlingFeeApplied": false,
"sendAmount": 103.25,
"receiveAmount": 100,
"totalFee": 3.25,
"partnerFee": 3, // Not present if partner fee is not enabled
"handlingFee": 0.25
},
"transferOut": {
"currency": "ETH",
"sendAmount": 0.009582,
"receiveAmount": 0.008582,
"networkFee": 0.001 // Not present if no network fee
},
"exchangeRate": 10436.23
}
Example response (buy) for
GET /trades/quote/:id/details?transferInMedium=card&transferOutMedium=blockchain&promoCode=12314code4321
{
"transferIn": {
"currency": "EUR",
"isMinimumHandlingFeeApplied": false,
"isMaximumHandlingFeeApplied": false,
"sendAmount": 103.25,
"receiveAmount": 100,
"totalFee": 3.25,
"partnerFee": 3, // Not present if partner fee is not enabled
"handlingFee": 0.25
},
"transferOut": {
"currency": "ETH",
"sendAmount": 0.009582,
"receiveAmount": 0.008582,
"networkFee": 0.001, // Not present if no network fee
"promoCodeValue": {
"amount": 10,
"currency": "EUR"
},
"promoCodeRedeemedAmount": {
"amount": 0.001,
"currency": "ETH"
}
},
"exchangeRate": 10436.23
}
Example response (sell) for
GET /trades/quote/:id/details?transferInMedium=blockchain&transferOutMedium=bank
{
"transferIn": {
"currency": "BTC",
"sendAmount": 0.009582,
"receiveAmount": 0.009582
},
"transferOut": {
"currency": "EUR",
"isMinimumHandlingFeeApplied": false,
"isMaximumHandlingFeeApplied": false,
"sendAmount": 103.25,
"receiveAmount": 100,
"totalFee": 3.25,
"partnerFee": 3, // Not present if partner fee is not enabled
"handlingFee": 0.25
},
"exchangeRate": 10775.41
}
GET https://app-api.coinify.com/trades/quote/:id/details
Endpoint should be used to get an overview of fees and amounts, before creating the trade
Request
The parameters in the request object are as follows:
Parameter | Type | Default | Description |
---|---|---|---|
transferInMedium |
String | Required | Transfer medium |
transferOutMedium |
String | Required | Transfer medium |
promoCode |
String | Optional | Used to validate if promo quote is valid and get new amounts |
Response (buy)
Key | Type | Description |
---|---|---|
transferIn |
Object | |
→currency |
String | |
→sendAmount |
Number | The amount the trader has to pay |
→receiveAmount |
Number | The amount we receive without the fees |
→handlingFee |
Number | The fee we charge for the payment |
→partnerFee |
Number | Partner fee (only present if enabled for partner) |
→totalFee |
Number | Handling fee + partner fee, same as the difference between sendAmount and receiveAmount |
→isMinimumHandlingFeeApplied |
Boolean | True if handling fee is the same as the minimum fee |
→isMaximumHandlingFeeApplied |
Boolean | True if handling fee is the same as the maximum fee |
transferOut |
Object | |
→currency |
String | |
→sendAmount |
Number | The amount before network fee |
→receiveAmount |
Number | The amount the trader receives |
→networkFee |
Number | Network fee for the transaction |
→promoCodeValue |
Object | {amount, currency} of the promo code value in FIAT (only present if promoCode is provided) |
→promoCodeRedeemedAmount |
Object | {amount, currency} of the promo code value in Crypto (only present if promoCode is provided) |
exchangeRate |
Number | Exchange rate used for this trade |
Response (sell)
Key | Type | Description |
---|---|---|
transferIn |
Object | |
→currency |
String | |
→sendAmount |
Number | The amount the trader has to pay |
→receiveAmount |
Number | The amount we receive without the fees |
transferOut |
Object | |
→currency |
String | |
→sendAmount |
Number | The amount we send without the fee |
→receiveAmount |
Number | The amount the trader receives after fees are subtracted |
→handlingFee |
Number | The fee we charge for the payout |
→partnerFee |
Number | Partner fee (only present if enabled for partner) |
→totalFee |
Number | Handling fee + partner fee, same as the difference between sendAmount and receiveAmount |
→isMinimumHandlingFeeApplied |
Boolean | True if handling fee is the same as the minimum fee |
→isMaximumHandlingFeeApplied |
Boolean | True if handling fee is the same as the maximum fee |
exchangeRate |
Number | Exchange rate used for this trade |
Create trade
Example request for
POST /trades
, using a valid price quote
{
"priceQuoteId": 123456,
"transferIn": {
"medium": "card"
},
"transferOut": {
"medium": "blockchain",
"details": {
"account": "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa"
}
},
"partnerContext": {
"clientId": 123,
"refId": 12
}
}
Example response for the above request
{
"id": 113475347,
"traderId": 754035,
"traderEmail": "customer@coinify.com",
"state": "awaiting_transfer_in",
"inCurrency": "USD",
"outCurrency": "BTC",
"inAmount": 1000.00,
"outAmountExpected": 2.41526674,
"transferIn": {
"currency": "USD",
"sendAmount": 1000.00,
"receiveAmount": 1000.00,
"medium": "card",
"details": {
"redirectUrl": "https://provider.com/payment/d3aab081-7c5b-4ddb-b28b-c82cc8642a18"
}
},
"transferOut": {
"currency": "BTC",
"medium": "blockchain",
"sendAmount": 2.41526674,
"receiveAmount": 2.41526674,
"details": {
// Trader's bitcoin address that will receive approx. 2.41526674 BTC if trade completes
"account": "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa"
}
},
"quoteExpireTime": "2016-04-01T12:38:19Z",
"updateTime": "2016-04-01T12:27:36Z",
"createTime": "2016-04-01T12:23:19Z"
}
Example request for
POST /trades
(buy bitcoins for bank transfer)
{
"priceQuoteId": 123456,
"transferIn": {
"medium": "bank"
},
"transferOut": {
"medium": "blockchain",
"details": {
"account": "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa"
}
}
}
Example response for the above request
{
"id": 113475347,
"traderId": 754035,
"traderEmail": "customer@coinify.com",
"state": "awaiting_transfer_in",
"inCurrency": "USD",
"outCurrency": "BTC",
"inAmount": 1000.00,
"outAmountExpected": 2.41526674,
"transferIn": {
"currency": "USD",
"sendAmount": 1000.00,
"receiveAmount": 1000.00,
"medium": "bank",
"details": { // Information about where the user should sent the money
"account": {
"currency": "DKK", // Currency of the bank account
"bic": "HIASDMIASD", // Account bic/swift/reg number depending on the type
"number": "1234-34235-3324-2342" // Account number
},
"bank": {
"name": "Bank name", // Name of the bank
"address": { // Address of the bank
"street": "123 Example Street",
"zipcode": "12345",
"city": "Exampleville",
"state": "CA",
"country": "US"
}
},
"holder": {
"name": "John Doe", // Name of the account holder
"address": { // Address of the account holder
"street": "123 Example Street",
"zipcode": "12345",
"city": "Exampleville",
"state": "CA",
"country": "US"
}
}
}
},
"transferOut": {
"currency": "BTC",
"medium": "blockchain",
"sendAmount": 2.41526674,
"receiveAmount": 2.41526674,
"details": {
// Trader's bitcoin address that will receive approx. 2.41526674 BTC if trade completes
"account": "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa"
}
},
"quoteExpireTime": "2016-04-01T12:38:19Z",
"updateTime": "2016-04-01T12:27:36Z",
"createTime": "2016-04-01T12:23:19Z"
}
POST https://app-api.coinify.com/trades
The Create trade endpoint allows a trader to trade crypto for fiat, or vice-versa.
On success, the endpoint returns the newly created trade object.
The endpoint supports the following directions:
- Buy crypto currency with card (
transferIn.medium == "card"
andtransferOut.medium == "blockchain"
) - Buy crypto currency with bank transfer (
transferIn.medium == "bank"
andtransferOut.medium == "blockchain"
) - Sell bitcoins and receive money by bank transfer (
transferIn.medium == "blockchain"
andtransferOut.medium == "bank"
)
Request object
The parameters in the request object are as follows:
Parameter | Type | Default | Description |
---|---|---|---|
priceQuoteId |
Integer | Required | Identifier of valid price quote to base the trade on. Passing an invalid price quote will result in an error. |
transferIn |
Object | Required | An object describing how Coinify will receive the money for this transfer. |
→medium |
String | Required | Transfer medium. |
→details |
Object | Optional | Can be used to supply returnUrl for medium=card . Transfer details. |
transferOut |
Object | Required | An object describing how Coinify will send the result of the trade to the trader. |
→medium |
String | Required | Transfer medium. |
→mediumReceiveAccountId |
Integer | Required if medium is bank | Bank account of the trader. |
→ details |
Object | Required if medium is blockchain | Transfer details |
partnerContext |
JSON | Optional | Partner context which will be returned in trade webhooks. Can be one of object, array, string, number, true, false as defined here https://www.json.org/json-en.html |
HTTP Response code | JSON data |
---|---|
201 Created |
Success, object returned, as shown to the right |
401 Unauthorized |
Error, access token missing. |
List trades
Example response for
GET /trades
[
{
"id": 113475347,
"traderId": 754035,
"traderEmail": "customer@coinify.com",
"state": "awaiting_transfer_in",
"inCurrency": "USD",
"outCurrency": "BTC",
"inAmount": 1000.00,
"outAmountExpected": 2.41526674,
"transferIn": {
"currency": "USD",
"sendAmount": 1000.00,
"receiveAmount": 1000.00,
"medium": "card",
"details": {
"redirectUrl": "https://provider.com/payment/d3aab081-7c5b-4ddb-b28b-c82cc8642a18"
}
},
"transferOut": {
"currency": "BTC",
"medium": "blockchain",
"sendAmount": 2.41526674,
"receiveAmount": 2.41526674,
"details": {
// Trader's bitcoin address that will receive approx. 2.41526674 BTC if trade completes
"account": "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa"
}
},
"quoteExpireTime": "2016-04-01T12:38:19Z",
"updateTime": "2016-04-01T12:27:36Z",
"createTime": "2016-04-01T12:23:19Z"
},
{
// Next trade...
}
]
GET https://app-api.coinify.com/trades
Response
This endpoint lists all trade objects belonging to the authorized trader.
The trades are ordered by id
(highest first).
HTTP Response code | JSON data |
---|---|
200 OK |
Success, response as shown to the right. |
400 Bad request |
Error interpreting the request. |
404 Not found |
Error, trades that belong to the provided trader were not found. |
Query parameters
Parameter | Type | Description |
---|---|---|
limit |
String | Limit used for pagination (default: 100, max: 100) |
offset |
String | Offset used for pagination (default: 0) |
trade_state |
String | Return only trades in specific state(s) (optional: will include all states by default) |
Get trade information
Example response for
GET /trades/113475347
{
"id": 113475347,
"traderId": 754035,
"traderEmail": "customer@coinify.com",
"state": "awaiting_transfer_in",
"inCurrency": "USD",
"outCurrency": "BTC",
"inAmount": 1000.00,
"outAmountExpected": 2.41526674,
"transferIn": {
"currency": "USD",
"sendAmount": 1000.00,
"receiveAmount": 1000.00,
"medium": "card",
"details": {
"redirectUrl": "https://provider.com/payment/d3aab081-7c5b-4ddb-b28b-c82cc8642a18"
}
},
"transferOut": {
"currency": "BTC",
"medium": "blockchain",
"sendAmount": 2.41526674,
"receiveAmount": 2.41526674,
"details": {
// Trader's bitcoin address that will receive approx. 2.41526674 BTC if trade completes
"account": "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa"
}
},
"quoteExpireTime": "2016-04-01T12:38:19Z",
"updateTime": "2016-04-01T12:27:36Z",
"createTime": "2016-04-01T12:23:19Z"
}
GET https://app-api.coinify.com/trades/<id>
This endpoint returns a trade object for the trade with ID <id>
.
Response
See trade object.
HTTP Response code | JSON data |
---|---|
200 OK |
Success, response as shown to the right. |
400 Bad request |
Error interpreting the request. |
404 Not found |
Error, trade is not found (no trade with such ID exists or no trade with such ID that belongs to the provided trader). |
Cancel trade
Example response for
PATCH /trades/113475347/cancel
{
"id": 113475347,
"traderId": 754035,
"traderEmail": "customer@coinify.com",
// State is now "cancelled"
"state": "cancelled",
"inCurrency": "USD",
"outCurrency": "BTC",
"inAmount": 1000.00,
"outAmountExpected": 2.41526674,
"transferIn": {
"currency": "USD",
"sendAmount": 1000.00,
"receiveAmount": 1000.00,
"medium": "card",
"details": {
"redirectUrl": "https://provider.com/payment/d3aab081-7c5b-4ddb-b28b-c82cc8642a18"
}
},
"transferOut": {
"currency": "BTC",
"medium": "blockchain",
"sendAmount": 2.41526674,
"receiveAmount": 2.41526674,
"details": {
"account": "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa"
}
},
"updateTime": "2016-04-01T12:34:37Z",
"createTime": "2016-04-01T12:23:19Z"
}
PATCH https://app-api.coinify.com/trades/<id>/cancel
This endpoint cancels the trade in the awaiting_transfer_in
state and returns a trade object for the cancelled trade.
Request object
This endpoint requires no request body
Response
See trade object.
HTTP Response code | JSON data |
---|---|
200 OK |
Success, response as shown to the right. |
400 Bad request |
Error trade not cancellable. |
404 Not found |
Error, trade is not found (no trade with such ID exists or no trade with such ID that belongs to the provided trader). |
Testing
This section explains how you can complete trades that you create in the Sandbox environment.
Before being able to complete the trades, make sure you have succesfully completed these actions and provided the following mandatory trader information:
- Verify trade account email
- Complete/Skip trader identification process
- Provide trader’s address
- Provide Source of Funds
- Provide PEP (Politically Exposed Person)
This section contains the following endpoints that allow you to test the full end-to-end trade flow:
- Force complete trade
- Complete trade with test card
- Complete trade with a fake bank transaction
- Complete/Skip KYC
Force complete trade
Example request for
POST /trades/113475347/test/complete-trade
{
"details": {
"account": "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa"
},
"amount": 200.00, // Transfer out amount of the fake transaction
"currency": "EUR" // Transfer out currency of the amount
}
POST https://app-api.sandbox.coinify.com/trades/<id>/test/complete-trade
This endpoint will complete a newly created trade which is in the awaiting_transfer_in
state.
Provide the ID of the trade in the <id>
path parameter to complete the specific.
Request object
The parameters in the request object are optional and are use to validate the trade if they are sent:
Parameter | Type | Default | Description |
---|---|---|---|
amount |
Float | (Optional) | Transfer out receive amount of the trade. |
currency |
String | (Optional) | Currency of the amount. |
details |
Medium details | (Optional) | Information relevant for this medium |
Details object
Response
This endpoint returns 204 No Content
if the trade is completed
successfully.
HTTP Response code | JSON data |
---|---|
204 No Content |
Success, empty response. |
400 Bad request |
Error interpreting the request. |
500 Server error |
Error error has occurred parsing the data. |
Complete trade with a test Card payment
Use the following test cards to complete a Buy trade via card payment.
Card Number | Name on card | Expected Result |
---|---|---|
4000027891380961 |
FL-BRW1 |
Card payment Successful |
4000027891380961 |
CL-BRW1 |
Challenge flow (3DS) |
4000164166749263 |
FL-BRW1 |
Do not honour |
5001638548736201 |
FL-BRW1 |
Declined |
4000020951595032 |
ERR-BRW1 |
Error |
Complete trade with a fake bank transaction
Example request for
POST /trades/113475347/test/bank-transfer
{
"sendAmount": 200.00, // Amount of the fake transaction
"currency": "EUR" // Currency of the amount
}
POST https://app-api.sandbox.coinify.com/trades/<id>/test/bank-transfer
This endpoint creates a fake bank transfer for the trade with ID <id>
and processes the trade as if a real bank transfer had occurred.
Request object
The parameters in the request object are as follows:
Parameter | Type | Default | Description |
---|---|---|---|
sendAmount |
Integer | Required | Amount sent from the trader’s bank account. This corresponds to the transferIn.sendAmount of the trade. |
currency |
String | Required | Currency of the amount. |
Response
This endpoint returns 204 No Content
if the fake bank transfer creation succeeded.
HTTP Response code | JSON data |
---|---|
204 No Content |
Success, empty response. |
400 Bad request |
Error interpreting the request. |
500 Server error |
Error error has occurred parsing the data. |
Complete/Skip KYC
In the sandbox environment it is possible to automatically approve an identification-attempt by including state: 'approved'
in the request object.
See example on the right or follow a more detailed explanation in this article.
Example of request to approve identification attempt
{
"returnUrl": "https://mypage.com/ida_complete",
"state": "approved"
}
Bank accounts
This section contains the create, list, get and delete end-points of the bank accounts used when creating a trade and to make payouts:
Bank account object
Example of a bank account object:
{
"id": 1234,
"account": {
"name": "my account",
"type": "danish", // Type of bank account
"currency": "DKK", // Currency of the bank account
"bic": "HIASDMIASD", // Account bic/swift/reg number depending on the type
"number": "1234-34235-3324-2342" // Account number
},
"bank": {
"address": {
"country": "US"
}
},
"holder": {
"name": "John Doe", // Name of the account holder
"address": { // Address of the account holder
"street": "123 Example Street",
"zipcode": "12345",
"city": "Exampleville",
"state": "CA",
"country": "US"
}
},
"update_time": "2016-04-01T12:27:36Z",
"create_time": "2016-04-01T12:23:19Z"
}
Key | Type | Description |
---|---|---|
id |
Integer | Identifier for the bank account |
account |
Object | Object with additional information about the bank account. |
→name |
String | Optional name for the trader to identify the account. |
→type |
bank account type | Type of the bank account. |
→currency |
String | Currency of the bank account |
→bic |
String | For sepa and international its the SWIFT / BIC number and for danish accounts its the REG number. |
→number |
String | For sepa and international it’s the IBAN (International Bank Account Number). For danish accounts, it’s the BBAN (Basic Bank Account Number). |
bank |
Object | Object with additional information about the bank. |
→address |
Object | Object with information about the address of the bank. |
→country |
String | ISO 3166-1 alpha-2 country code |
→state |
String | State (Required for country US). |
holder |
Object | Object with additional information about the bank account holder. |
→name |
String | Name of the bank account holder. |
→address |
Object | Object with information about the address of the account holder. |
→street |
String | Street address. |
→zipcode |
String | Zip/Postal code. |
→city |
String | City. |
→state |
String | State. (Required for country US) |
→country |
String | ISO 3166-1 alpha-2 country code |
updateTime |
ISO 8601 time | The time when the bank account was last updated. |
createTime |
ISO 8601 time | Timestamp for when this bank account was first created. |
Bank account types
The bank accounts have three different types depending on the account currency (account.currency
) and the bank country (bank.address.country
).
When creating a new bank account, you don’t explicitly specify the type; instead Coinify decides the type for you, based on the requirements in the following table:
Type | Description | Requirements |
---|---|---|
danish |
Danish national bank account | Country must be DK and currency DKK . |
sepa |
SEPA bank account | Country must be in our list of SEPA countries, and currency must be EUR . |
international |
International bank account | Everything else. |
Create bank account
Example request to
POST /bank-accounts
{
"account": {
"name": "my bank account",
"currency": "DKK", // Currency of the bank account
"bic": "6456", // Account REG number
"number": "12345435345345" // Account number
},
"bank": {
"address": { // Address of the bank
"country": "DK"
}
},
"holder": {
"name": "John Doe", // Name of the account holder
"address": { // Address of the account holder
"street": "123 Example Street",
"zipcode": "12345",
"city": "Exampleville",
"state": "CA",
"country": "US"
}
}
}
Example response for the above request
{
"id": 12345, // Identifier of the bank account
"account": {
"name": "my bank account",
"type": "danish", // Type of bank account
"currency": "DKK", // Currency of the bank account
"bic": "6456", // Account bic/swift/reg number depending on the type
"number": "12345435345345" // Account number
},
"bank": {
"address": { // Address of the bank
"country": "DK"
}
},
"holder": {
"name": "John Doe", // Name of the account holder
"address": { // Address of the account holder
"street": "123 Example Street",
"zipcode": "12345",
"city": "Exampleville",
"state": "CA",
"country": "US"
}
},
"update_time": "2016-04-01T12:27:36Z",
"create_time": "2016-04-01T12:23:19Z"
}
POST https://app-api.coinify.com/bank-accounts
On success, the endpoint returns the newly created bank-account object.
Some of the fields are only required if the bank account is of type international see account-types.
How to create a bank account in Sandbox
To create a bank account in Sandbox you can use the fake account details from the example request body on the right. You can also create a fake bank account with your own preferred details. However, bank accounts in sandbox must follow the basic bank account rules from the real world for a particular country. Follow these guidelines to create your own fake bank account in sandbox:
- The bank account currency must be supported by Coinify. You can retrieve this info by calling the Payment Methods endpoint.
- The Bank SWIFT/BIC code must contain a valid country code on the 5th and 6th character of the code itself, e.g
XXXXDKXX
and the valid length is 8 or 11 chars. - IBAN account number should start with a valid 2-letter country code. The IBAN country code must match the country code from which the bank’s SWIFT/BIC code originates. E.g. if the bank’s BIC code is from Denmark, the IBAN should start with “DK”.
- The IBAN must be from a Coinify supported country. See Supported Countries list.
- The IBAN account number must have a length which is valid for the specific country.
You can easily look up the SWIFT/BIC bank codes and IBAN basic structure for a specific country online.
Request object
The parameters in the request object are as follows:
Parameter | Type | Default | Description |
---|---|---|---|
account |
Object | Required | Object with additional information about the bank account. |
→name |
String | Optional | Optional name for the trader to identify the account. |
→currency |
String | Required | Currency of the bank account. |
→bic |
String | Required | For sepa and international it’s the SWIFT / BIC number and for danish accounts it’s the REG number. |
→number |
String | Required | For sepa and international it’s the IBAN (International Bank Account Number). For danish accounts, it’s the BBAN (Basic Bank Account Number). |
bank |
Object | Required for non-SEPA payments | Object with additional information about the bank. |
→address |
Object | Required | Object with information about the address of bank. |
→country |
String | Required | ISO 3166-1 alpha-2 country code |
→state |
String | State (Required for country US). | |
holder |
Object | Required | Object with additional information about the bank account holder. |
→name |
String | Required | Name of the bank account holder. |
→address |
Object | Required | Object with information about the address of the account holder. |
→street |
String | Required | Street address. |
→zipcode |
String | Optional | Zip/Postal code. |
→city |
String | Required | City. |
→state |
String | Required | State. Required for country US |
→country |
String | Required | ISO 3166-1 alpha-2 country code. |
HTTP Response code | JSON data |
---|---|
201 Created |
Success, response as shown to the right. |
400 Bad request |
Error, interpreting the request (missing parameters). |
401 Unauthorized |
Error, access token missing. |
HTTP status code | Error code | Description |
---|---|---|
400 |
missing_argument |
There was something wrong with the signup request. See the error_description argument for a specific, human-readable error message. |
400 |
invalid_argument |
There was something wrong with the signup request. See the error_description argument for a specific, human-readable error message. |
400 |
invalid_iban |
Provided account.number is an invalid IBAN. This is only for SEPA countries. |
List bank accounts
Example request for
GET /bank-accounts
[
{
"id": 12345, // Identifier of the bank account
"account": {
"type": "danish", // Type of bank account
"currency": "DKK", // Currency of the bank account
"bic": "6456", // Account bic/swift/reg number depending on the type
"number": "12345435345345" // Account number
},
"bank": {
"address": { // Address of the bank
"country": "DK"
}
},
"holder": {
"name": "John Doe", // Name of the account holder
"address": { // Address of the account holder
"street": "123 Example Street",
"zipcode": "12345",
"city": "Exampleville",
"state": "CA",
"country": "US"
}
},
"update_time": "2016-04-01T12:27:36Z",
"create_time": "2016-04-01T12:23:19Z"
},
{
// Next bank-account...
}
]
GET https://app-api.coinify.com/bank-accounts
On success, the endpoint returns a list of bank-account objects for the current trader.
The accounts are ordered by id
(highest first).
HTTP Response code | JSON data |
---|---|
200 OK |
Success, response as shown to the right. |
401 Unauthorized |
Error, access token missing. |
404 Not found |
Error, no bank accounts found. |
Get bank account
Example request for
GET /bank-accounts/12345
{
"id": 12345, // Identifier of the bank account
"account": {
"type": "danish", // Type of bank account
"currency": "DKK", // Currency of the bank account
"bic": "6456", // Account bic/swift/reg number depending on the type
"number": "12345435345345" // Account number
},
"bank": {
"address": { // Address of the bank
"country": "DK"
}
},
"holder": {
"name": "John Doe", // Name of the account holder
"address": { // Address of the account holder
"street": "123 Example Street",
"zipcode": "12345",
"city": "Exampleville",
"state": "CA",
"country": "US"
}
},
"update_time": "2016-04-01T12:27:36Z",
"create_time": "2016-04-01T12:23:19Z"
}
GET https://app-api.coinify.com/bank-accounts/<id>
On success, the endpoint returns a bank-account object.
HTTP Response code | JSON data |
---|---|
200 OK |
Success, response as shown to the right. |
401 Unauthorized |
Error, access token missing. |
403 Forbidden |
Error, the supplied access token doesn’t have the rights to delete this bank-account. |
404 Not found |
Error, bank-account is not found. |
Delete bank account
DELETE https://app-api.coinify.com/bank-accounts/<id>
Deletes a bank account.
HTTP Response code | Description |
---|---|
204 No Content |
Success, empty response. |
401 Unauthorized |
access token missing. |
403 Forbidden |
The supplied access token doesn’t have the rights to delete this bank-account. |
404 Not found |
bank-account is not found. |
Payment page
The payment page is the default go-to solution when paying for a trade with a credit/debit card - It easy to integrate, it works on web, mobile web and native mobile and covers the common use-cases to let your customers pay for crypto currencies.
The user journey of the payment page is built with safety and simplicity in mind. Your app opens the payment page in an iframe, webview or browser tab. The user inputs his credit card details and our services authenticates the payment or denies the transaction. The entire payment flow takes a 2-3 minutes for the user to complete.
How to use
When you create a trade with the input medium set to card, a redirectUrl will be returned which needs to be opened up inside an iframe.
- Create a trade
- A redirectUrl will be returned. See more in Trade object
- Open redirect url. (See example to the right)
- Handle successful or failed payment attempt.
Return URL
It is crucial that during trade creation you provide a returnURL
the payment page will redirect itself to upon successful or denied payment attempt.
E.g. supplied returnURL = https://mywallet.com/payment-return
Payment result | Return URL |
---|---|
Success |
https://mywallet.com/payment-return?status=success |
Failure |
https://mywallet.com/payment-return?status=failed |
When this option is used it will be up to you to implement and host a relevant way of handling the return that makes sense for the given application.
Webhooks
This section describes the webhooks that are sent to partners’ backend systems when certain events occur to entities controlled by the partner.
Webhooks perform signed HTTP POST
requests about specific events to a URL of your choice.
You can find the list of IP addresses from which Coinify’s system sends webhook events here: IP address list
Webhook structure
This section describes the general structure of the webhooks that you will receive.
// Example webhook payload
{
"id": "bd21c0e7-ddb6-4f8e-9367-a6ca00eca25c",
"time": "2017-09-14T09:07:11.335Z",
"event": "identification-attempt.approved",
"context": {
"traderId": "420"
}
}
All webhooks are sent as JSON, and shares the same general structure as described in the following table:
Key | Type | Description |
---|---|---|
id |
String (UUID v4) | Unique identifier for the event. Retries for the same events will share the same uuid . |
time |
String (ISO-8601 timestamp) | Timestamp for when the event has occurred. |
event |
String | Event that occurred. See Webhook events for a list of possible events. |
context |
Object | Context for this event. Structure is defined by the event as described in [Webhook events].(#webhook-events). |
Webhook events
This section provides a list of possible events that can be received as webhooks
Context of identification-attempt.* events
{
"traderId": "1",
"rejectReason": "DENIED" // Only for rejected event
}
Context of trade events (trade.rejected, trade.cancelled, trade.expired)
{
"id": "1235",
"traderId": "123",
"residenceCountry": "DK",
"partnerContext": {
"clientId": 123,
"refId": 12
}
}
Context of trade events (trade.created, trade.transfer-in-completed, trade.completed)
{
"id": "1235",
"traderId": "123",
"residenceCountry": "DK",
"eurAmount": 100,
"transferIn": {
"medium": "card",
"amount": {
"amount": 103,
"currency": "EUR"
},
"totalFee": {
"amount": 3,
"currency": "EUR"
}
},
"transferOut": {
"medium": "blockchain",
"amount": {
"amount": 0.099,
"currency": "BTC",
"isApproximate": false // If this is set, we cannot guarantee the amount or fee
},
"totalFee": {
"amount": 0.0001,
"currency": "BTC"
},
"details": {
"address": "btc-address",
"transaction": "btc-tx-id"
}
},
"partnerContext": {
"clientId": 123,
"refId": 12
}
}
Context of otc-trade.completed event
{
"id": "1234-1234",
"time": "yyyy-mm-ddThh:mm:ss.msZ",
"event": "otc-trade.completed",
"context": {
"traderId": "123",
"residenceCountry": "DK",
"id": "1234",
"partnerFee": {
"amount": {
"amount": 100.00,
"currency": "EUR"
}
},
"totalFee": {
"amount": {
"amount": 200.00,
"currency": "EUR"
}
},
"transferIn": {
"amount": {
"amount": 30000,
"currency": "EUR"
},
"details": {
"address": "abcd",
"transaction": "efgh"
},
"transactionTime": "yyyy-mm-ddThh:mm:ss.msZ"
},
"transferOut": {
"amount": {
"amount": 1.000,
"currency": "BTC"
},
"details": {
"address": "ijkl",
"transaction": "mnop"
},
"transactionTime": "yyyy-mm-ddThh:mm:ss.msZ"
},
"createTime": "yyyy-mm-ddThh:mm:ss.msZ"
}
}
Event | Description |
---|---|
identification-attempt.approved |
Identification attempt has been approved |
identification-attempt.rejected |
Identification attempt has been rejected. See Identification attempt rejection reason for possible reject reasons. |
trade.created |
Trade has been created |
trade.completed |
Trade has been completed |
trade.transfer-in-completed |
Trade transfer in completed and waiting for transfer out to be sent |
trade.cancelled |
Trade has been cancelled |
trade.expired |
Trade has been expired |
trade.rejected |
Trade has been rejected |
otc-trade.completed |
OTC trade has been completed. The only event possible for OTC trades. |
How to respond to a webhook request
This section describes how your system should respond to an incoming webhook request
If you respond with a 2xx
code, our system will consider the webhook as successfully sent and received.
If you respond with another status code than 2xx
, our system will consider the webhook request as a failure and retry at a later time. See Retrying failed requests for more information about the retry strategy.
If the webhook signature is incorrect, you should consider replying just as you would if the signature was correct, to avoid attackers being able to brute-force the shared secret.
Retrying failed requests
This section describes how failed webhook requests will be retried
We will use exponential backoff for handling webhook retries.
Retry attempts | Retry Interval |
---|---|
16 | 16 sec |
And use this formula to calculate next retry attempt, which will result in last attempt after ~6 days from first failure.
next_retry_attempt = retry_interval * 2^retry_count
Webhook signature
Node pseudo-code to validate signature
const crypto = require('crypto');
const sharedSecret = 'shared-secret';
// Express example
const body = req.body;
const signature = req.headers['X-Coinify-Webhook-Signature'];
const hash = crypto.createHmac('sha256', sharedSecret)
.update(JSON.stringify(body))
.digest('hex');
return hash === signature;
Python pseudo-code to validate signature
import hashlib, hmac
shared_secret = 'the_shared_secret'
# Get the raw HTTP POST body (JSON object encoded as a string)
body = get_body()
# Get the signature from the HTTP or email headers
signature = get_header("X-Coinify-Webhook-Signature")
expected_signature = hmac.new(shared_secret, msg=body, digestmod=hashlib.sha256).hexdigest()
return signature == expected_signature
All webhooks sent from Coinify are signed with a shared secret that is known only by you and Coinify. This ensures the integrity of the data contained in the webhook, and also proves that Coinify is the sender of the webhook (provided the shared secret is not known by anyone else).
Specifically, the signature uses HMAC-SHA256, using the shared secret as the key and the full HTTP request body (UTF-8 encoded) as the message. The resulting signature is provided in lowercase hexadecimal format in the X-Coinify-Webhook-Signature
HTTP header.
Signature example
Use the following example to test that your signature validation function is working correctly
Shared secret: my-shared-secret
, Payload: {"examplePayload":true}
Expected signature: bcdbb89e3031905f3cc1a20d16b5f969a17a7d8fa0c26e4a807c2193402d66f4
Websocket
This section describes the WebSocket service that can be used to push real-time notifications directly to the browsers of traders.
Overview
Example JavaScript code for connecting and authenticating as trader to WebSockets
const authenticationToken = 'valid-authentication-token-without-Bearer-prefix';
const url = 'wss://app-ws.coinify.com/ws/trader';
const websocket = new WebSocket(url);
websocket.onopen = event => {
console.log({type: 'open', event});
// You must send this message immediately after connection opened
// as well as for each new authenticationToken obtained
websocket.send(JSON.stringify({type: 'authenticate', token: authenticationToken}));
};
websocket.onerror = event => {
console.log({type: 'error', event});
};
websocket.onclose = event => {
console.log({type: 'close', event});
};
websocket.onmessage = event => {
console.log({type: 'message', event});
const message = JSON.parse(event.data);
if (message.type === 'authenticated') {
const { traderId } = message;
console.log('Authenticated as trader #' + traderId);
} else if (message.type === 'event') {
const { event, context } = message;
console.log('Received event ' + event + ' with the following context:');
console.log(context);
}
};
The WebSocket service is available at wss://app-ws.coinify.com/ws/trader
.
To the right you can see a basic example of how to connect and authenticate to the WebSocket service. The example is not complete - e.g. it does not ping the service at regular intervals as described in pinging section.
Authentication
Example message authenticating the connection
{
"type": "authenticate",
"token": "<access token>"
}
Response on valid authentication
{
"type": "authenticated",
"traderId": "12345"
}
Once connected, the connection must immediately be authenticated with an access token as described in the authentication section in the beginning of the API documentation. The connection will be closed by the service with status code 3109
after 30 seconds if the client fails to authenticate as a trader.
Furthermore, the connection must also be re-authenticated before the currently used access token expires. Connection closes automatically when authentication token expires with status code 3130
if you don’t re-authenticate before.
On every successful authentication, the server responds with a message with type: 'authenticated'
as seen on the right.
Message format
All messages sent back and forth are strings containing JSON objects. JSON objects sent to the server must contain a type
field, which must contain one of the following values:
Client-to-server message types
type |
Description |
---|---|
authenticate |
Authenticates as a trader. Object must also hold a token field, containing an access token. See authentication section above for more information. |
ping |
Pings the server in order to keep the connection alive. See pinging section below. |
Furthermore, the server can send messages of the following types:
Server-to-client message types
type |
Description |
---|---|
authenticated |
Your connection was successfully authenticated. See authentication section above for more information. |
pong |
Response to a ping message from the client. See pinging section below. |
event |
An event related to the authenticated trader occurred. The message will also contain an event field and possibly other fields as well. See trader events section below. |
Pinging
Example ping message
{
"type": "ping"
}
Response on ping
{
"type": "pong"
}
Close status codes
Closing a WebSocket connection requires the server to give a numeric status code (See also RFC 6455: 7.4. Status Codes).
The following table shows all possible status codes (in addition to those defined in RFC 6455):
Code | Reason |
---|---|
30xx | Message error |
3000 | Message received containing invalid JSON |
3005 | Message object received without type field |
31xx | Authentication failure |
3101 | Message object with type: 'authenticate' received without token field |
3109 | Failure to authenticate within time limit |
3110 | Invalid authentication token |
3120 | Illegal re-authentication by different trader |
3130 | Authentication token expired |
3140 | Client tried to ping without being authenticated |
In addition to the status code, a JSON-encoded reason
object is also returned containing two fields:
Field | Description |
---|---|
error |
Machine-readable error string. Each status code has a distinct error code, which contains a slightly more informational message than just the numeric code. |
error_description |
Human-readable error string |
Trader events
This section lists the trader-related events that can be received through our WebSocket service
email-verified
identification-attempt.approved
identification-attempt.rejected
trade.transfer-in-received
email-verified
Example
email-verified
event
{
"type": "event",
"event": "email-verified"
}
The trader verified their email address by clicking the link in the verification email.
identification-attempt.approved
Example
identification-attempt.approved
event
{
"type": "event",
"event": "identification-attempt.approved"
}
The trader identification attempt was approved.
identification-attempt.rejected
Example
identification-attempt.rejected
event
{
"type": "event",
"event": "identification-attempt.rejected",
"context": {
"rejectReason": "ERROR_NOT_READABLE_ID.MISSING_SIGNATURE"
}
}
The trader identification attempt was rejected. The event object also contains the following fields:
Field | Description |
---|---|
rejectReason |
Identification attempt rejection reason |
Trade events
This section lists the trade-related events that can be received through our WebSocket service
trade.transfer-in-received
Example
trade.transfer-in-received
event
{
"type": "event",
"event": "trade.transfer-in-received",
"context": {
"tradeId": "1234"
}
}
Transfer in was received (but not confirmed yet). Can be used to tell if the bitcoin transaction has been seen by us (Coinify)
Field | Description |
---|---|
tradeId |
Reference to the trade |
Trade widget
Trade Widget is a single page app that implements a full trade solution to be embedded in an iframe.
By default, it implements a full customer onboarding flow, but it can also be combined with our API to create a custom onboarding flow. This can be useful e.g. in mobile wallets where the user is already onboarded in the mobile app.
<iframe src="https://trade-ui.sandbox.coinify.com?partnerId={replace-with-assigned-id}&primaryColor=blue&cryptoCurrencies=BTC,ETH,XLM" width="100%" height="576px" allow="camera;fullscreen;accelerometer;gyroscope;magnetometer" allowfullscreen></iframe>
Example to embed widget in iframe for production
<iframe src="https://trade-ui.coinify.com?partnerId={replace-with-assigned-id}&primaryColor=blue&cryptoCurrencies=BTC,ETH,XLM" width="100%" height="576px" allow="camera;fullscreen;accelerometer;gyroscope;magnetometer" allowfullscreen></iframe>
Example to pass partnerContext as stringified json
<iframe src="https://trade-ui.coinify.com?partnerId={replace-with-assigned-id}&partnerContext=%7B%22clientId%22%3A123%2C%22refId%22%3A12%7D" width="100%" height="576px" allow="camera;fullscreen;accelerometer;gyroscope;magnetometer" allowfullscreen></iframe>
⚠️ To embed the widget into your web page you must add an iframe element as shown in the examples to the right. |
---|
For user identity verification the following attributes are required to be specified for the iframe element:
⚠️ The allow="camera;fullscreen;accelerometer;gyroscope;magnetometer" allowfullscreen attributes must be included to enable the camera and provide the best user experience for user identity verification. |
---|
⚠️ In case you are nesting the iframe in another iframe the allow="camera;fullscreen;accelerometer;gyroscope;magnetometer" allowfullscreen attributes must be added to every iframe. |
---|
The widget supports a number of configuration options defined using query parameters.
⚠️ The partnerId query parameter must be set to the proper value so that users can be registered with your company |
---|
Customised Trade Widget landing page
There is also an option to create a custom landing web page with a dedicated URL for your Trade Widget. The customised Trade Widget landing page allows you to specify the following parameters in order to modify the landing page appearance according to your requirements:
- Partner Title - defines the title of the browser tab.
- Partner Name - specifies the /path part of the landing page URL. E.g. https://trade.coinify.com/PartnerName
- Primary Color - defines the colour of buttons on the Trade Widget.
- Light Color and Dark Color - define colours of the gradient background behind the widget.
- Logo - is an .svg file of your logo that you share with Coinify to set on the customised landing page.
- Link - URL to your official website that is set behind the Logo.
Check out the picture bellow to get a better understanding of which part of the customised landing page do the above mentioned parameters affect.
If you want to set up your customised trade widget landing page, make a request in your dedicated communication channel with Coinify and provide all of the necessary parameters. You can also find example values for the mentioned parameters that you need to provide to Coinify below.
- Partner Title= Crypto Company
- Partner Name = cryptoCompany
- Primary Color= #4231c8
- Light Color= #4231c8
- Dark Color= #150e4f
- Partner Logo = YourLogo.svg
- Partner Link= https://www.cryptocompany.com
Supported query parameters
⚠️ Every parameter has to be encoded to be used in the URL. E.g. using encodeURIComponent(yourParameter) in JS. |
---|
⚠️ By passing some of the parameter values in the Trade Widget URL, specific Trade Widget UI screens will be skipped, effectively simplifying the customer’s flow. Please find detailed explanation of which UI screens are skipped based on the provided parameters: |
---|
- Passing the
transferInMedia
will set the payment method accordingly and the customer will not have to select the payment method again on Coinify’s Trade Widget. - If
buyAmount
,defaultFiatCurrency
, anddefaultCryptoCurrency
are provided, the Quote UI will be skipped. - Providing the
address
value will skip the screen where the customer is asked to provide the receiving crypto address.
If you’re already collecting any of this datapoints on your end, it is recommended to pass these values to Coinify’s Trade Widget for a more seamless customer experience. We encourage you to test the flow yourself to get a better understanding of the flow with and without providing these parameters.
Parameter | Description |
---|---|
partnerId |
id you got from Coinify |
partnerName |
name to be displayed in the widget as partner name, currently used when displaying partner fee if enabled, defaults to ‘Partner’. Can be string or object (just has be be provided as url encoded string, see example) |
partnerContext |
Free context for partners to receive in webhooks, can be stringified JSON as well. Partner context may also be provided using settings.partner-context-changed |
primaryColor |
color of the primary button; Accepted: named value e.g. blue , hash value e.g. #00ff00 , or rgba(0,255,0,1) |
cryptoCurrencies |
comma separated list of crypto currencies to support in the widget. Will include only the listed crypto currencies from those supported by default |
fiatCurrencies |
comma separated list of fiat currencies to support in the widget. Will include ony the listed fiat currencies from those supported by default |
defaultCryptoCurrency |
defines the crypto currency initially selected on Buy/Sell quotes if multiple crypto currencies are available |
defaultFiatCurrency |
defines the fiat currency initially selected on Buy/Sell quotes if multiple fiat currencies are available |
buyAmount |
defines initial amount on Buy quotes. Note, often used with targetPage , defaultCryptoCurrency , and defaultFiatCurrency defined |
sellAmount |
defines initial amount on Sell quotes. Note, often used with targetPage , defaultCryptoCurrency , and defaultFiatCurrency defined |
isBuyAmountFixed |
If set to true , the specified buyAmount is locked and cannot be changed from the Trade Widget UI. Accepts true or false values. |
isSellAmountFixed |
If set to true , the specified sellAmount is locked and cannot be changed from the Trade Widget UI. Accepts true or false values. |
address |
defines default wallet address to receive funds at when buying crypto currencies, and default wallet address to refund funds to when selling crypto currencies in case the sell cannot be completed. See details on how to use address below. |
addressSignature |
provides HMAC-SHA-256 signature for address . Only used if a shared secret has been established with Coinify. See details on how to use address below. |
confirmMessages |
defines if external confirmation of trade messages is required. When provided the widget will await confirmation messages from hosting window before continuing flow. See details on how to use messages below. |
memo |
defines an optional memo/destination tag accepted by some crypto currencies. See details on how to use address below. |
noSignup |
defines whether to support signup through the widget. Note the negation! Available values are 'false’ (allow signup for corporate & individuals), 'true’ (allow no signup), 'corporate’ (allow signup only for individual traders) and 'individual’ (allow signup only for corporate traders). |
targetPage |
defines a target landing page other than the default. Available values are 'login’, 'signup’, 'signup-corporate’, 'buy’, 'sell’ and 'trade-history’ |
refreshToken |
used for custom onboarding flows. The refeshToken can be obtained by calling one of the auth endpoints. The refreshToken should be URL encoded so that the browsers interprets it correctly. |
transferInMedia |
list of transfer media for inbound payment method. Available: card, bank, blockchain. See details on how to use below. |
transferOutMedia |
list of transfer media for outbound payment method. Available: bank, blockchain. See details on how to use below. |
returnURL |
the URL that you want to redirect the customer to upon successfuly completed trade. The provided URL must be url-encoded. Note that this query parameter is only available for the Customised Landing Page URL. |
NOTE providing these parameters can only remove options from the users view. There may be reasons (e.g legal or contractual) that some options are not available to your users in the underlying API. Adding parameters here will not change that.
In general the widget will adjust to only present what makes sense with the given parameters (ie remove the option to buy if no relevant transfer media are found). It is currently possible to specify parameters that result in an invalid configuration. If you experience a blank widget, try to remove some parameters.
How to use address
<!-- Examples to provide default wallet address for multiple currencies and for single currency -->
<iframe src="...?address=BTC:2N7gzbQsDnfAFdwabcFqyw1Q8y1ZUpeqUgD,ETH:0x44eae1E05F5f294f0f2a054D16605993FCd627a9&..." />
<iframe src="...?cryptoCurrencies=BTC&address=2N7gzbQsDnfAFdwabcFqyw1Q8y1ZUpeqUgD&..." />
The address
parameter should be provided in the format currency1:address1,currency2:address2,...
to specify default
wallet address per cryptocurrency. Alternatively, if the widget is configured to have only a single currency, you may
provide only the address and omit the currency. See examples.
When buying a cryptocurrency the address provided for the currency is used to pre-fill the wallet address input field. Equally, when selling, the address is used to pre-fill the refund wallet address input field, (note, some cryptocurrencies does not require a refund address to be provided). Buying or selling currencies with no default address specified requires the user to manually enter the wallet address.
Address signature
// Javascript example of how to generate signature from secret and address
const crypto = require('crypto');
const address = 'crypto-address';
const secret = 'shared-secret';
const accountSignature = crypto.createHmac('SHA256', secret).update(address).digest('hex');
In order to ensure that the address passed into the widget has not been tampered with, we have the possibility for you to sign the address(es) using a shared secret, and passing the signature to the widget as well.
Address signature follows the same format as the address
parameter described above:
- Single currency:
- Format:
addressSignature=<sig>
...?cryptoCurrencies=BTC&address=2N7gzbQsDnfAFdwabcFqyw1Q8y1ZUpeqUgD&addressSignature=<sig>
- Format:
- Multiple currencies
- Format:
addressSignature=BTC:<btcSig>,ETH:<ethSig>
...?address=BTC:<btcAddr>,ETH:<ethAddr>&addressSignature=BTC:<btcSig>,ETH:<ethSig>
- Format:
Memo
Some crypto currencies accept a memo/destination tag in addition to the wallet address. The value for this can be specified in the memo
query parameter.
Memo
follows the same format as the address
and addressSignature
parameters, see examples above.
How to use transferInMedia
and transferOutMedia
These parameters have default values and omitting them from the query is equivalent to enabling all available transfer media.
By default the user is able to: 1. Buy crypto using all available payment methods 2. Sell crypto to all available payout methods
By limiting the transfer media available, the payment methods available will also change. This can be used to limit what is possible for the end user in terms of buying & selling as illustrated by the examples below.
Examples showing how to limit payment methods
Use case | Parameters | Comment |
---|---|---|
Disable sell, allow buy via card or bank | transferInMedia=card,bank |
Since blockchain is not available as transfer in media, it’s not possible to sell crypto |
Disable sell, allow buy bank only | transferInMedia=bank |
Further restrict the allowed transfer in options |
Disable sell, allow all buy options | transferOutMedia=blockchain |
If new transfer in media become available, they will be enabled |
Disable buy, allow all sell options | transferInMedia=blockchain |
If new transfer out media become available, they will be enabled |
Custom onboarding flow
By default, the widget includes a full customer onboarding flow. Depending on the use-case, this may be all that is needed.
But if a different user experience is needed, the widget plays well with the API and in general adopts to use information provided via the API. The API endpoints below might be useful
- Signing up a trader - You likely want to use Trusted validation if you have already validated the user
- Obtaining a
refreshToken
- You would need an offline token stored with your user’s information to obtain this - Provide personal information
- Provide source of funds
- Provide KYC information (only if enabled in your contract)
Widget Sizes
Trade Widget is designed with responsiveness in mind. It will adjust its layout to the size of a screen it is displayed at.
Width
Currently supported layouts include:
Device type | min Width | max Width |
---|---|---|
Mobile | 320px | 575px |
Tablet | 576px | 768px |
Computer | 769px | none |
Height
The minimal recommended height of the widget is 576px. Increasing the height will result in stretching the widget, as well as hiding scrollbars on text heavy pages like e.g. Transaction Details.
Widget messages
To send and receive events add a script into your web page as in the example:
<script >
// receive events
window.addEventListener("message", receiveMessage, false);
function receiveMessage(event) {
if (event.origin === 'https://trade-ui.coinify.com' ||
event.origin === 'https://trade-ui.sandbox.coinify.com'
) {
const widgetMessage = event.data;
// your message handler code goes here ...
// e.g.
if (widgetMessage.event === 'trade.trade-prepared') {
// here you could validate the used crypto address
}
}
// send events
const iframe = document.getElementById('your-widget-iframe-id');
iframe.contentWindow.postMessage({
type: 'event',
event: 'trade.confirm-trade-prepared',
context: {
confirmed: true
}
}, '*');
</script>
The window.postMessage()
method is used to send cross-origin messages from the widget to the hosting window.
Similarly, window.addEventListener()
is used to receive cross-origin messages from the hosting window.
Address confirmation
A common use case for messages is to allow the hosting window to validate the used cryptocurrency address used for a trade.
For this use case, the widget is loaded using the confirmMessages=true
in the query parameters. The hosting
window listens for trade.trade-prepared
and trade.trade-created
, and once received it validates if the
cryptocurrency address used in the trade is as expected, and then responds by posting trade.confirm-trade-prepared
and
trade.confirm-trade-created
respectively with context telling widget whether trade is accepted or declined.
Message format
All messages objects have a type property. Currently, only a single type event
is available:
Message types
type |
Description |
---|---|
event |
An event related to the widget occurred. The message will have an event property and optionally a context property. See widget events section below. |
Message events
Example messages
{
"type": "event",
"event": "trade.trade-prepared",
"context": {
"baseAmount": 50,
"baseCurrency": "EUR",
"quoteAmount": 0.00187817,
"quoteCurrency": "BTC",
"transferIn": {
"medium": "card"
},
"transferOut": {
"details": {
"account": "ms357UPjpLkQjKifoRKjfsk5Zxedd14uZm"
},
"medium": "blockchain"
}
}
}
{
"type": "event",
"event": "trade.trade-created",
"context": {
"createTime": "2021-01-21T15:12:20.300Z",
"id": 151551,
"inAmount": 50,
"inCurrency": "EUR",
"isPriceQuoteApproximate": false,
"outAmountExpected": 0.00187817,
"outCurrency": "BTC",
"quoteExpireTime": "2021-01-21T15:26:43.396Z",
"state": "awaiting_transfer_in",
"traderEmail": "john@doe.com",
"traderId": 1234,
"transferIn": {
"id": 461378,
"sendAmount": 53.25,
"receiveAmount": 50,
"currency": "EUR",
"medium": "card"
},
"transferOut": {
"id": 461379,
"sendAmount": 0.00187817,
"receiveAmount": 0.00160017,
"currency": "BTC",
"medium": "blockchain",
"details": {
"account": "ms357UPjpLkQjKifoRKjfsk5Zxedd14uZm"
}
},
"updateTime": "2021-01-21T15:12:20.300Z"
}
}
{
"type": "event",
"event": "trade.trade-placed",
"context": {
"tradeId": 123456
}
}
{
"type": "event",
"event": "trade.confirm-trade-prepared",
"context": {
"confirmed": true
}
}
{
"type": "event",
"event": "trade.confirm-trade-created",
"context": {
"confirmed": true,
"tradeId": ,
"transferInitiated": true
}
}
{
"type": "event",
"event": "settings.partner-context-changed",
"context": {
"partnerContext": {
"yourPartnerContext": "here"
}
}
}
The following message events may be sent from the widget
The following message events may be sent to the widget
trade.trade-prepared
A trade has been prepared for review. Sent when the trader has reviewed the transaction, but before actually creating a
trade in the backend. The message context
contains a partial trade object excluding some
properties which are not available until the trade is created in the backend.
trade.trade-created
A trade has been created in the backend. Sent when the trader has reviewed the transaction
(and for bank trades has completed KYC) and continues to complete payment.
The message context
contains a trade object.
trade.trade-placed
The trader has placed a trade. Sent when a user has completed placing a trade (buy or sell) and clicks to return to the
quote page. The message context
object has the following properties:
Property | Description |
---|---|
tradeId |
id of the trade placed |
trade.confirm-trade-prepared
The trader has accepted or rejected the prepared trade. When using the parameter
confirmMessages
this event must be sent after a user has either accepted or rejected the
prepared trade. The message context
object has the following properties:
Property | Description |
---|---|
confirmed |
the confirmation status, true if accepted or false if rejected |
trade.confirm-trade-created
The trader has accepted or rejected the created trade. When using the parameter
confirmMessages
this event must be sent after a user has either accepted or rejected the
created trade. The message context
object has the following properties:
Property | Description |
---|---|
confirmed |
the confirmation status, true if accepted or false if rejected |
tradeId |
id of the trade placed |
transferInitiated |
sell trades only, optional, true if the hosting page itself has initiated a transfer, and widget should skip the transfer crypto page |
settings.partner-context-changed
The partner context has changed. Sent by the hosting window in case partner context has changed.
The provided value will overwrite any current value. Note, a default value may be set
using the partnerContext
query parameter.
The message context
object has the following properties:
Property | Description |
---|---|
partnerContext |
Free context object |
Trade Widget Sandbox Testing
In order to test trades in the Trade Widget, use the widget’s sandbox environment base URL. Transactions completed in the sandbox environment have no value. Complete flow for Buy trades is available only for USDC on Ethereum's Sepolia testnet
and BTCt on BTC testnet3 network
. However, the complete Sell trades flow in sandbox is available only using the BTC testnet3 network.
Currency | Network | Wallet Setup Tutorial |
---|---|---|
USDC | Sepolia testnet | Metamask Sepolia and USDC Setup. Recommended for Buy Trade testing. |
BTCt | Testnet3 | Bitcoin Testnet Wallet Setup. Recommended for Sell Trade testing. |
Use test credit card details to complete card buy trades
Use fake bank transfer API endpoint to complete bank buy trades
Buy Button
Buy Button is a simple web component that opens up trade widget in a 768x576 popup.
How to use
Add the following code into your web page: for sandbox
<script src="https://trade-ui.sandbox.coinify.com/components/buy-button.js"></script>
...
<buy-button
url="https://trade-ui.sandbox.coinify.com?partnerId={replace-with-assigned-id}&primaryColor=blue&cryptoCurrencies=BTC,ETH,XLM"
/>
for production
<script src="https://trade-ui.coinify.com/components/buy-button.js"></script>
...
<buy-button
url="https://trade-ui.coinify.com?partnerId={replace-with-assigned-id}&primaryColor=blue&cryptoCurrencies=BTC,ETH,XLM"
/>
url property points to the trade widget just as in the example.
⚠️ The partnerId queryString parameter must be set to the proper value so that users can be registered with your company |
---|
Styling the Buy Button
Custom CSS properties exposed
Property | Description |
---|---|
background-color |
color of the button |
color |
color of the text |
Style using custom CSS properties
<style type="text/css">
buy-button {
--background-color: #8fc065;
}
</style>
Style using CSS
buy-button button {
background-color: #8fc065;
}