1. Overview
Section Data Delivery Interface describes the Data Delivery Interface and provides background information on the gaming data reporting.
Section API resources gives an overview of existing API resources in the Data Delivery Interface.
Section API attributes describes API resources attributes.
Section Environments and API keys describes environments, API keys and allowed IP addresses.
Section Sending data to DDI describes how to use the Data Delivery Interface and its resources.
Section Large files discusses submission of large files in detail. It also describes how to connect and upload data to SFTP servers.
Section Schemas describes the validation schemas.
Section API responses discusses API responses to data submission and what actions need to be taken in some special cases.
Section Delivering files describes how to deliver small files and large files. The requirements for handling HTTP responses is also described in this section.
Section Error handling goes through the error handling process. Hereafter it explains the handling of API errors and other errors from the Error Bucket. The procedure to resend data after an error has occurred is presented here as well.
Section Contact Information provides the contact information.
Section Glossary presents a glossary.
Section Appendix includes the methods in the Data Delivery Interface and examples of data structures.
2. Data Delivery Interface
In order to be able to provide gambling services in Denmark it is required to be licensed by the Danish Gambling Authority. Furthermore, all the gaming data has to be sent to the Danish Gambling Authority as well.
Since Danske Spil is a licensed gambling provider, it is required to report to Danish Gambling Authority. However Danske Spil game suppliers has to report the data to Danske Spil and not to the Danish Gambling Authority directly.
Danske Spil provides a Data Delivery Interface (DDI) to enable its game suppliers to transfer gaming data to Danske Spil. Game suppliers are required to send the gaming data to DDI in order to be compliant with Danish law.
The DDI is developed and supported by team SAFE at Danske Spil. Hence "reporting to SAFE" or "submitting data to SAFE" is the same as "reporting to DDI".
This document describes the DDI and information on the integration to the DDI. The document covers topics such as REST API for delivering gambling records and retrieving data schema specifications, the Error Bucket for retrieving errors on delivered gambling records, sending large files over SFTP that exceed 1 MB
2.1. Standard Records
The specification on how the data should be reported to the Danish Gambling Authority is called Standard Records. The DDI follows Standard Records and adds additional functionality, such as a possibility to send the data in json format.
Even though Danske Spil game suppliers have to report gaming data to the DDI, all the information on the game details, formatting, related fields, etc is specified in the Standard Records and not the DDI. Hence the DDI can be understood as a proxy to report to the Danish Gambling Authority.
| It is important to get familiar with both the DDI and the Standard Records documentation. |
The Standard Records is a set of XML schema definition (XSD) files that describe how the gaming data has be reported to the Danish Gambling Authority.
2.1.1. Workflow in Standard Records
The Standard Records describe a "workflow" of a game.
To illustrate an idea of a "workflow", let’s look into a specific example of a number game (talspil). For number games the Danish Gambling Authority has designed 5 schema definitions:
-
Start structure definition schema
-
Transaction structure definition schema
-
End-of-day structure definition schema
-
End-of-game structure definition schema
-
End structure definition schema
Each of these definition schemas describe a particular part of the game. And the purpose of the Standard records is to document the stages in the workflow of a game with the help of the definition schemas. The definition schemas contain many different types of information including winnings, commissions, amount of purchases, currencies, play identifications, etc.
Let’s look again into a specific example of a number game and relate to above-mentioned definition schemas:
-
A new number game draw (lottery) is created, typically with a duration of 1 week. This requires a Start structure to be reported.
-
Customers can buy tickets to the draw. And for every purchase a Transaction structure has to be sent.
-
After a certain amount of time, it is no longer possible to buy tickets to this draw. In other words, the draw becomes closed. This requires a End-of-game structure to be reported.
-
The number game draw is performed, and the winners of the draw are being identified. This requires an End structure to be reported. And for each day when it is possible to buy into the number game draw, an End-of-day structure has to be reported.
As mentioned above the supplier reports to Danske Spil via DDI and not to the Danish Gambling Authority. However DDI reuses Standard Records schema definitions and provides additional functionality.
2.1.2. JSON and English element names
All the XSD schema definitions defined by the Danish Gambling Authority use Danish language both the element names and element documentation. However the DDI from Danske Spil introduces JSON schemas with field names and documentation translated to English. The JSON schemas correspond exactly with the original XSD schema definitions from Standard Records, except for some header information.
Hence a supplier can choose between sending data in either XML using original element names in Danish or JSON using translated English element names. The DDI will internally translate the JSON format into XML with original element names and will validate the translated XML against the corresponding XSD schema definition.
XSD and JSON schemas are discussed in detail in section Schemas. However it is recommended to read the chapters in sequence.
2.2. API Evolution Requirements
While the DDI API undergoes changes very infrequently, it is essential to note that adding new fields to the response message is considered a backward-compatible change. Therefore, your implementation must be accommodating of unknown fields in responses. This applies to all API endpoints.
3. API resources
DDI is a REST API consisting of several resources. REST API resources presented below are discussed in detail in other sections and this section should be considered just as an overview.
| This section does not discuss API keys, environments and other important attributes that should be a part of a request to the DDI. This is discussed in later sections. |
3.1. Data submission
Data submission is discussed in detail in section Sending data to DDI. This section only summarises the API endpoint.
Data has to be sent to DDI via POST requests. The data has to be either in XML or JSON formats and has to conform to corresponding XSD schema definition or JSON schema. And the data has to be sent differently for payloads larger than 1 MB
POST /SAFE/{payloadType}/{gameCategory}/view/{gameType}/{supplier}/{sequenceId}/{transactionId}?{queryParameters} HTTP/1.1
The attributes such as payloadType, gameCategory, etc are described in section API attributes.
The actual sent payload and the validation is discussed in chapter Schemas.
3.2. Retrieving errors
Handling errors id described in detail in section Error handling.
Errors in data submission are handled differently in DDI for different errors. For instance requests with invalid payload or payload not confirming to the XSD schema or JSON schema will be rejected immediately with non-200 HTTP response. However certain errors such as logical errors in the payload are not rejected immediately. That is DDI will return a 200 response as the request may pass initial validation. All the requests passing initial validation are validated in detail within several minutes. If DDI encounters a logic error in the detailed validation it will create an error in Error Bucket. Supplier is obliged to check Error Bucket once every minute.
| Data submission requests with a non-200 response are considered rejected. Such requests will not create an error in Error Bucket. Error explanation will be directly in the response. |
Error Bucket is a GET request to DDI which returns a list of errors.
It can be used to retrieve all errors or only errors that were created after a certain error.
To retrieve all errors pass an empty in errorId parameter.
However, only first
1000
errors will be fetched.
So if there are more than
1000
pass the last error id in errorId as a parameter to receive next errors as described below.
errorId can be set to a specific error id.
Then DDI will return all the errors that were created after specified errorId.
But again no more than
1000
errors will be returned.
To get more errors pass the last received error id to get a next errors section.
| The amount of returned errors is limited to 1000 |
For instance setting errorId to last received error id will return a list of all new errors that might have been created.
GET /SAFE/errors/{supplier}/{errorId} HTTP/1.1 (1)
| 1 | Same as with the data submission supplier is the id of the supplier |
GET /SAFE/errors/MySupplier HTTP/1.1
a5920000-b5ad-11ea-8080-808080808080GET /SAFE/errors/MySupplier/a5920000-b5ad-11ea-8080-808080808080 HTTP/1.1
| Use last received error id when getting new errors. Then only new errors will be received. |
3.3. Updating Errors
Errors can be updated with ticket-id, proposed resolution via the API using the following API call: ./SAFE/errors/update/{supplier}/{errorId}
| Parameter | Description |
|---|---|
|
The unique ID for the transaction supplier. Each supplier only has access to one ID’s resource. You should get your ID supplied from Danske Spil. |
|
The unique UUID |
Optional query parameters:
| Parameter | Description |
|---|---|
|
The supplier’s ticket id |
|
The proposed resolution |
Request:
$ curl 'https://api.prod.safe.dsservice.eu/SAFE/errors/update/TEST/87fd121e-f840-11f0-938e-aff13f5c3186?resolution=Resolution&ticketId=TEST-1234' -i -X PUT \
-H 'x-api-key: XXX' \
-H 'Content-Type: application/x-www-form-urlencoded'
Response:
{
"responseCode" : "SUCCESS",
"responseMessage" : "Error record updated successfully",
"responseID" : "87fd121e-f840-11f0-938e-aff13f5c3186"
}
3.4. Retrieving schemas
As it will be discussed in detail in section Data submission the data sent to DDI has to conform to either XSD schema definition or JSON schema definition depending on the format of data being sent.
A zip file containing all schemas, both xml and json is available for download.
4. API attributes
This section describe API attributes such as gameCategory, supplier, etc that are used throughout the document.
API attribute in the context of this document is meant a part of the request to the DDI. For instance data submission which discussed in detail in section Sending data to DDI requires certain API attributes be a part of the request url.
POST /SAFE/{payloadType}/{gameCategory}/view/{gameType}/{supplier}/{sequenceId}/{transactionId}?{queryParameters} HTTP/1.1
Some of the attributes need to be present in the payload sent to the DDI. This is discussed in detail in later chapters. Hence API attribute can be thought of as a piece of information sent along with the request.
Note, the attribute values are case sensitive.
| API attributes values are case sensitive |
4.1. Supplier
Every Danske Spil gaming data supplier has a unique supplier which has to be a part of data submission and error fetching endpoints.
The supplier is constant and does not change over time.
Danske Spil will issue a supplier during onboarding process.
4.2. PayloadType
Denotes the type of payload being sent to the data submission endpoints.
Can either be xml or json.
If payloadType is set to xml then the submitted payload has to be in XML format.
And if it is set to json then the payload has to be in JSON format.
This is valid both when submitting small and large files.
However when submitting large files the body of the request will contain only the reference to the referenced file in JSON format, also for the XML files.
See section Large files for information on large file submission.
4.3. GameCategory
Indicates which game category is being used.
Can either be monopol or other.
Danske Spil will inform which game category to specify.
In short monopol denotes that the gaming data relates to the monopoly games that only Danske Spil is licensed to issue.
Whilst game category other has to be used for all other gaming data.
4.4. GameType
Defines which type of gaming data is being sent.
This could be for example FastOddsSlutStrukturType or EndOfDayRapportStrukturType.
Note that the same game type is be different for different payloadType.
For xml payloadType the gameType will be in Danish, as it follows more-less directly the Standard Records.
For json payloadType the gameType will be in English.
As mentioned above gameType follow data structures defined by the Standard Records (see section Standard Records).
Thus it is a good idea to check DGA Standard Records documentation regarding game type of the gaming data being sent.
A list of possible game types along with a reference to DGA documentation can be found in section Available schemas.
4.5. SequenceId
SequenceId was a feature intended to help detect missing transactions, but it was never implemented and never will be. For backwards compatability though it is still part of the API, and it’s value should just be hardcoded to 1-1.
4.6. TransactionId
In the context of the DDI, a transaction refers to a gaming data payload accompanied by various attributes, such as SupplierId, GameType, TransactionId and others. A key part of each transaction is the TransactionId, which uniquely identifies the transaction sent to the DDI. Every new transaction must have a distinct TransactionId to ensure it is recognized as unique by the DDI.
If the same TransactionId is reused for multiple transactions, the system will reject it.
However if a transaction isn’t sent successfully for various reasons, and the sending system attempts to send it again, with exactly the same content, then it should not be assigned a new TransactionId
transactionId has to uniquely identify a transaction sent to the DDI.
And the same transaction can have only one unique transactionId.
|
transactionId has a special format and consists of three sub-attributes: verticalId, timestamp and uid.
transactionId{transactionId} = {verticalId}-{timestamp}-{uid}
transactionId{transactionId} = MyVerticalKey-1451649600-9d988a9170b7a9fd
verticalId and timestamp are valuable attributes that ease troubleshooting issues with the gaming data
4.6.1. verticalId
verticalId enables a virtual transaction partitioning.
This can be used for example when data is being sent from different environments to the same DDI environment.
Or it could be used to virtually distinguish different types of gaming data being sent to the DDI.
verticalId is a constant string that can contain alphanumeric characters and _.
Danske Spil coordinates creation of a new verticalId.
A supplier cannot freely choose a verticalId or send random values in verticalId as it may lead to data inconsistency.
4.6.2. Timestamp
timestamp is a unix time timestamp in seconds.
It has to be set to the time of transaction payload generation.
For instance 2016-01-01T12:00:00+00:00 time would correspond to unix time 1451649600.
4.6.3. UID
uid is a random 64-bit random value in hex, for example f988a9170b7a9fa0.
This mean that a uid is always 16 characters long.
Note that all characters have to be lower-cased.
4.7. errorId
errorId is a unique error id that identifies an error from the Error Bucket.
errorId is generated by the DDI.
When the DDI encounters an error while processing submitted game data it creates an error with a unique errorId.
errorId can be used to retrieve list of errors from Error Bucket.
See further information in section Retrieving errors.
errorId is a time-based UUID (UUID Version 1), for example c6537392-68f8-11e6-8b77-86f30ca893d3.
5. Environments and API keys
The DDI has several environments.
| Environment | API host | SFTP host | Purpose |
|---|---|---|---|
Test default |
|
|
Test default |
Town 23 |
|
|
Town 23 |
Town 31 |
|
|
Town 31 |
Town 33 |
|
|
Town 33 |
Town 35 |
|
|
Town 35 |
Town 36 |
|
|
Town 36 |
Town 42 |
|
|
Town 42 |
Town 43 |
|
|
Town 43 |
Town 46 |
|
|
Town 46 |
Town 51 |
|
|
Town 51 |
Town 52 |
|
|
Town 52 |
Town 53 |
|
|
Town 53 |
Town 54 |
|
|
Town 54 |
Production |
|
|
Production environment |
| The Town environments were added September 2024. |
API Host is a host for all the DDI endpoints. SFTP Host is a host for uploading large files, which is explained in section Large files.
In the schematic examples in this document the Host part is usually omitted.
Thus for instance retrieving all errors for MySupplier in {supplier} in the test environment should be called in the following way.
MySupplier.GET /SAFE/errors/MySupplier HTTP/1.1 Host: api.test.safe.dsservice.eu (1) x-api-key: N9yy5ymPvShBmoVnotJrexiJzBfpwNfJOnTyTNMv (2)
| 1 | The Host part of the request which is usually be omitted in the examples in this document |
| 2 | Mandatory DDI API key which is discussed next |
To access an environment an API key has to be provided. And each environment usually needs to be accessed with a different API key. API keys are issues by Danske Spil. Always make sure to check with Danske Spil which API key should be used for which environment. All Town environments share the same API key
| API key might be different for different environment. Check with Danske Spil which API key should be used for which environment. |
The API key has to be supplied in x-api-key header in requests to DDI.
If API key is not provided or is invalid, then the request is rejected.
In addition to this the ip addresses of requests has to be added to allowed IP addresses list. Thus make sure to always inform Danske Spil about IP addresses change.
| IP address of the system accessing has to be added to the allow list at Danske Spil |
API key is not required retrieving schemas described earlier in section Retrieving schemas.
Otherwise API key is required accessing all other resources.
However, in examples in this document API key x-api-key header might be omitted for brevity.
6. Sending data to DDI
This section discusses integration to DDI.
The data to DDI is sent via REST API endpoints. The payload can either be in XML or JSON format, which should be valid according do DDI XSD or JSON Schema. Sending large payloads has to be done differently than smaller payloads. Section Schemas discusses further the XSD and JSON schemas. Section API responses discusses the data submission responses and actions that might needed to be taken.
The following sections describe particular details of the DDI integration. There are examples in section Small file examples that can be used for an inspiration.
6.1. Performing requests
The environments are described in section Environments and API keys.
The Host part of http requests will be omitted in the following examples.
The request path to DDI contains attributes that describe the payload being sent. The path and query parameters of the request contains information on type of payload (XML or JSON), supplier information, type of structure being submitted, additional flags or attributes in query parameters.
POST /SAFE/{payloadType}/{gameCategory}/view/{gameType}/{supplier}/{sequenceId}/{transactionId}?{queryParameters} HTTP/1.1
POST /SAFE/json/monopol/view/NumberGameEndStructure/TEST/22334455-24/my_transaction_20201001_100958-0-81a8bec826e7c0dd?large=yes HTTP/1.1
The request parameters are described in section API attributes. They will be discussed in more detail later on.
6.2. Small and large files
Integration to DDI is different for smaller and larger payloads.
DDI refers to smaller payloads as small files which cannot exceed 1 MB . Larger payloads are referred as large files which are above 1 MB
The difference between small files and large files is how the payload is being sent to DDI. All the other request attributes are nearly the same.
6.2.1. Small files
Small files should have a payload directly in the body of the request. Payloads can either be sent as XML or JSON.
POST /SAFE/xml/.. HTTP/1.1 (1) Host: test-api.safe.danskespi.dk x-api-key: N9yy5ymPvShBmoVnotJrexiJzBfpwNfJOnTyTNMv Content-Type: application/xml (2) <?xml version="1.0" encoding="UTF-8" standalone="no"?> (3) <FastOddsSlutStruktur xmlns="..
| 1 | Note xml in the request path since json is being submitted.
It would have to be json for the JSON. |
| 2 | Depending on the payload type the value of Content-Type header should be either application/xml or application/json |
| 3 | Payload is embedded directly into the request |
6.2.2. Large files
Section Large files discusses large files submission in detail.
Large files are sent slightly differently.
First of all the payload have to be uploaded to an SFTP server.
Same as with small files the payload can be both in XML or JSON.
Large file requests have to have a query parameter large=yes.
The reference to the uploaded payload has to be in body of the request in a specific json structure.
And the whole request has to with Content-Type: application/json.
POST /SAFE/{payloadType}/..?large=yes HTTP/1.1 (1)
Host: test-api.safe.danskespi.dk
x-api-key: N9yy5ymPvShBmoVnotJrexiJzBfpwNfJOnTyTNMv
Content-Type: application/json (2)
{ "path": "/202010/path_to_uploaded_payload_on_ftp" } (3)
| 1 | Depending on the type of file uploaded to the SFTP, payloadType has to be either xml or json.
Large file requests also have to have large=yes query parameter. |
| 2 | Content-Type always has to be application/json for large file requests |
| 3 | The reference to the uploaded payload on sftp server |
| The request will be rejected if is sent as a small file with a payload exceeding 1 MB . Requests exceeding 1 MB always have to be sent as large files. |
6.3. Small file examples
This section contains several examples of small files submission.
6.3.1. json format
EndOfDayReportStructure in json for supplier MySupplier to test environmentPOST /SAFE/json/other/view/EndOfDayReportStructure/MySupplier/22334455-24/my_test_20200115_094235-0-684c03e58dbee560 HTTP/1.1
Host: api.test.safe.dsservice.eu
x-api-key: N9yy5ymPvShBmoVnotJrexiJzBfpwNfJOnTyTNMv
Content-Type: application/json
{
"LicenseHolder": {
"EndOfDayReportDate": "2020-07-02",
"CurrencyInformationCode": "DKK"
},
"GameStatementList": [
{
"GameStatement": {
"GameCategoryName": "Bingospil",
"EndOfDayReportNumberOfGames": 8,
"EndOfDayReportDepositGame": "67.888",
"EndOfDayReportDepositJackpot": "1.552",
"EndOfDayReportWinnings": "42.15",
"EndOfDayReportCommissionRake": "29.76"
}
}
]
}
6.3.2. xml format
FastOddsTransaktionStruktur in xml for supplier MySupplier to test environmentPOST /SAFE/xml/other/view/FastOddsTransaktionStrukturType/MySupplier/11223344-21/my_test_20200617_094235-0-684c03e58dbed556 HTTP/1.1
Host: api.test.safe.dsservice.eu
x-api-key: N9yy5ymPvShBmoVnotJrexiJzBfpwNfJOnTyTNMv
Content-Type: application/xml
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<FastOddsTransaktionStruktur xmlns="http://skat.dk/begrebsmodel/2009/01/15/">
<FastOddsTransaktionStruktur>
<FilInformation>
<SpilFilVersion>v2</SpilFilVersion>
<SpilFilIdentifikation>123</SpilFilIdentifikation>
</FilInformation>
<TilladelsesindehaverOgSpil>
<SpilCertifikatIdentifikation>SAFE2_DanskeSpil</SpilCertifikatIdentifikation>
<SpilKategoriNavn>Fastoddsspil</SpilKategoriNavn>
</TilladelsesindehaverOgSpil>
<TransaktionListe>
<Transaktion>
<SpillerInformationIdentifikation>1188176</SpillerInformationIdentifikation>
<SpilTransaktionIdentifikation>O/0064351/0001980</SpilTransaktionIdentifikation>
<SpilKøbDatoTid>2020-06-18T11:09:26.588</SpilKøbDatoTid>
<SpilForventetSlutDatoTid>2020-06-19T11:59:59.588</SpilForventetSlutDatoTid>
<SpilSalgskanal>Mobil</SpilSalgskanal>
<SpilIndskud>79</SpilIndskud>
<ValutaOplysningKode>DKK</ValutaOplysningKode>
<SpilSted>
<SpilHjemmeside>www.danskespil.dk</SpilHjemmeside>
</SpilSted>
</Transaktion>
</TransaktionListe>
</FastOddsTransaktionStruktur>
</FastOddsTransaktionStruktur>
7. Large files
This section describes further details on large file submission. It is based on the background information that was present in the section Sending data to DDI.
| All payloads larger than 1 MB has to be submitted as large files. Otherwise the request to the DDI will be rejected. |
As it was discussed in section Sending data to DDI sending large files is quite similar to the sending small files. The difference with the large files is that first the payload has to be uploaded to a SFTP server. And then a large file pointer with a reference to the uploaded file has to be sent to the DDI. Same as with the small files the large files payload is to be valid according to the XSD or JSON schemas. This is discussed further in section Schemas. Furthermore, section API responses discusses responses to the data submission.
| A large file payload must be fully uploaded to SFTP prior to sending a submission request to the DDI. |
SFTP servers for each environment are listed in section Environments and API keys.
7.1. Connecting
Connection to the SFTP server is configured separately for each environment.
| Connection to the SFTP servers is configured separately for each environment. |
To connect to a SFTP server a supplier has to generate private - public key pair and send the public key to Danske Spil. Danske Spil issues a username and sets up the connection. All Town environments share the same Large file key
The generated public SSH key has to be in OpenSSH format.
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQDSNM6RVVmwN3y0NurIQnmZgjcx5K5zzZu9nDqopW4JIn/mr8OYZI3heSJShnIM8EThvwVGXXXyyJVRQAvRHYFO4DxS6bufSNWr3BxBGaGYlYxI9mgvQnT6+MzE3oZyEMdQNPlV5VfbileXlrPoAk1TkGdVdhwdLJMI2B4KUyMf+Q==
A step by step connection guide for one environment:
-
Generate a private / public RSA key pair for the environment
-
Send the public key to Danske Spil and inform for which environment this should be set up for
-
Receive a username for the environment from Danske Spil
-
Test the connection
| Username might be different for each environment. |
| There is no IP allow list for SFTP servers. Thus there is no need to inform Danske Spil where the SFTP uploads are coming from. |
7.2. SFTP Client requirements
The SAFE2 SFTP server will be regularly updated to follow best practices regarding accepted cryptographic algorithms (message authentication codes (MACs), key exchanges (KEXs), and cipher suites).
As of January 2025, we use the policy described in AWS TransferSecurityPolicy-2024-01.
It is important that you keep your large file sending software updated to support best practices.
To make testing if new cipher suites are compatible with your client, we recommend that you add a feature to you reporting application that lets you do a simple connect check against any host (without doing any authentication). Then you can from your production environment verify against the SAFE2 test environment (where new ciphers will be applied first).
7.3. Sending large files
7.3.1. Uploading a file
A file with the payload has to be uploaded to a folder /incoming/{yyyyMM} on the SFTP server.
The yyyyMM indicates the year and the month when the payload was uploaded to the server.
A file has to be named with transactionId in the base name of the file and payloadType as the extension.
/incoming/{yyyyMM}/{transactionId}.{payloadType}
json format on the SFTP server/incoming/202011/MyVerticalKey-1451649600-9d988a9170b7a9fd.json
7.3.2. Sending a large file pointer
Once the file has been uploaded to the SFTP server a supplier has to send a pointer to the uploaded large file to the DDI. The submission of the large file pointer has to happen to the same environment as the SFTP server (see section Environments and API keys). Sending large file pointers is almost the same as sending small files (see section Sending data to DDI) with the following differences.
-
queryParametersin the request have to include a parameterlarge=yes. -
The
Content-Typeheader of the request always have to beapplication/json. -
The body of the request has to have a pointer to the uploaded SFTP file in a format of
{ "path": "/{yyyyMM}/{transactionId}.{payloadType}" }.
POST /SAFE/{payloadType}/{gameCategory}/view/{gameType}/{supplier}/{sequenceId}/{transactionId}?large=yes{queryParameters} (1)
Host: .. (2)
x-api-key: ..
Content-Type: application/json (3)
{ "path": "/{yyyyMM}/{transactionId}.{payloadType}" } (4)
| 1 | Note a large=file query parameter |
| 2 | Host and API key are omitted for brevity |
| 3 | Content-Type header always has to be set to application/json when submitting large files |
| 4 | The body of the request as a large file pointer to the uploaded file on the SFTP server |
xml file pointer to the DDIPOST /SAFE/xml/monopol/view/NumberGameEndStructure/MySupplier/22334455-24/MyVerticalKey-1451649600-9d988a9170b7a9fd=yes HTTP/1.1 (1)
Host: api.test.safe.dsservice.eu
x-api-key: N9yy5ymPvShBmoVnotJrexiJzBfpwNfJOnTyTNMv
Content-Type: application/json (2)
{"path" : "/202011/MyVerticalKey-1451649600-9d988a9170b7a9fd.xml"} (3)
| 1 | Note xml in the payloadType |
| 2 | Content-Type header is still application/json even though the uploaded large file is in XML |
| 3 | Note xml in the file extension |
json file pointer to the DDIPOST /SAFE/json/monopol/view/NumberGameEndStructure/MySupplier/22334455-24/MyVerticalKey-1451649600-9d988a9170b7a9fd?large=yes HTTP/1.1 (1)
Host: api.test.safe.dsservice.eu
x-api-key: N9yy5ymPvShBmoVnotJrexiJzBfpwNfJOnTyTNMv
Content-Type: application/json
{"path" : "/202011/MyVerticalKey-1451649600-9d988a9170b7a9fd.json"} (2)
| 1 | Note json in the payloadType |
| 2 | Note json in the file extension |
7.4. How the DDI handles large files
Initially upon the data submission the DDI will check only the request attributes and verify that the referenced file in the large file pointer actually exists.
If no issues are found the DDI will then return 200.
However, the actual payload validation happens within several minutes.
Thus even though large file submission returns 200, it does not mean that the data submission was successful.
Same as with the small files, the large files have to conform to the corresponding XSD or JSON schemas that were defined in payloadType and gameType attributes in the request.
If the DDI encounters an error while processing a large file it will create an error in the Error Bucket. As was mentioned above the processing of large files is not immediate and may take several minutes. A game data supplier is obliged to check Error Bucket once a minute for new errors. See further information in section Error handling.
8. Schemas
The payload that is sent to the DDI has to conform to corresponding XSD or JSON schema.
XSD schemas follow standard records (see Standard Records) which are developed by DGA. However, it is also possible to send data in JSON using JSON schemas. The JSON schemas are developed and maintained by Danske Spil. The JSON schemas have the same logical structure as XSD schemas and use English terms instead of Danish.
JSON schemas are based on the draft JSON Schema: A Media Type for Describing JSON Documents.
A zip file containing all schemas, both xml and json is available for download.
8.1. Data Bundling
If a Standard record structure contains an array, it is recommended to bundle the data.
The DGA advices that purchases of the same type of game and draw should be bundled.
eg. instead of sending each game purchase as a transaction structure containing 1 purchase, the game purchases for a short period of time should be bundled, and send in the same transaction structure. So a transaction structure would contain more than 1 purchase.
The maximum period of bundling would/could be set to 1 minute with a limit of 100 purchases.
8.2. Datetime format
All dates/times/timestamps in the Standard Records structures must be reported in UTC.
Hence it is important that the EndOfDay calculation performed in the GameEngine is also calculated using UTC time, please see Transaction Reporting Isolation
8.3. Rewriting of SpilFilIdentifikation when sending as XML
When you send data in XML format, the SAFE2 system will replace the element SpilFilIdentifikation with the transactionId from the request URL,
before validating the schema.
So even though the schema mandates it, you are not required to fill in the SpilFilIdentifikation element.
Similar goes for the SpilFilErstatningIdentifikation element (used when sending replacements, see Replacement), which will automatically be added with the value of the resend URL parameter (if you send the SpilFilErstatningIdentifikation element, its contents will be replaced instead of being added).
8.4. Whitespace handling
Any leading or trailing whitespace within XML elements or JSON values is trimmed during processing before being sent to the DGA system.
For example, consider the following snippet, which illustrates how whitespaces are trimmed. Please note that since the DGA system only accepts XML-formatted data, any JSON data is automatically translated to XML.
| XML with whitespace | JSON with whitespace | Will be transformed to |
|---|---|---|
|
|
|
8.5. Which schemas to use
During the onboarding phase Danske Spil guides suppliers through the DDI integration.
And Danske Spil will inform how exactly the gaming data should be reported and which view schemas have to be used.
Once view schemas are known, it is a good idea to look explore the referenced class and type schemas to get a better understanding on how the data should be reported.
However, nearly always in addition to the reported gaming data a supplier has to report end-of-day and sometimes end-of-game structures.
These structures summarize the gaming data and transactions sent for the whole day or for the whole game.
End-of-day and end-of-game schemas exist both for xml and json payloadType, and both for other and monopol game categories.
It is a good idea to get familiarized with these schemas as well.
-
payloadTypexml-
gameCategorymonopol-
/SAFE/xsd/monopol/view/MonopolEndOfDayRapportStrukturType
-
-
gameCategoryother-
/SAFE/xsd/other/view/EndOfDayRapportStrukturType
-
-
-
payloadTypejson-
gameCategorymonopol-
/SAFE/json/monopol/view/EndOfDayReportStructure
-
-
gameCategoryother-
/SAFE/json/other/view/EndOfDayReportStructure
-
-
The list of all available schemas is given in Available schemas.
8.6. Schemas in different environments
Schemas are not changed often. Hence accessing schemas on different environments (see section Environments and API keys) will most likely have the same results. However, once DGA updates the XSD schemas, Danske Spil has to develop JSON schema changes as well. And then Danske Spil publishes the new XSD and JSON schemas in the DDI. First the schemas are published to Test environment, then to UAT and then to Production. Hence there might be period when different environments have a difference in schemas.
It is important to point out that the DDI respects the schemas that are published in a given environment. That is the DDI will perform the payload validation according to the version of the schemas that is published in the given environment.
9. API responses
This section discusses API responses during data submission (see sections Sending data to DDI and Large files). Even though data submission returns "standard" HTTP codes back, there are several things to be aware of.
The DDI returns a standard set of HTTP response codes back to the data submission:
-
200OK -
4xxClient error -
5xxServer error
The response always includes an additional information in the body.
200 HTTP response){
"responseCode": "SUCCESS",
"responseMessage": "Validation succeeded, message accepted for further processing.",
"responseID": "17dcbf0b-7857-4b4c-89f7-fe18b6ea9b31"
}
400 HTTP response){
"responseCode": "ERROR",
"responseMessage": "ERR020 - JSON parse error",
"responseID": "cf190f25-cbaa-46e0-914f-b95749beeb73"
}
Note that responseMessage in both successful and unsuccessful request provides further information.
9.1. 200 OK
This means that the DDI has receive a request and there is no immediate validation error. However this does not mean that there are no error in the submitted request. As mentioned in sections Sending data to DDI and Large files it can take up to several minutes to fully process the request. That is the in-deep validation and processing of the submitted data does not happen immediately.
A 200 HTTP response does not mean that the data has been processed.
It mean only that there are no errors found in the initial validation.
|
After the in-deep validation and processing, the DDI may reject the request. This can be for instance related to the logic error in the transaction. In this case the DDI will create an error in the Error Bucket. The supplier is required to check the Error Bucket every minute and handle new errors. See more on the error handling in section Error handling.
| The supplier is required to check Error Bucket every minute for new errors. |
9.2. 400 Client error
This means that the DDI has rejected the submitted transaction immediately. No error will be created in the Error Bucket. The supplier has to correct the request and send it again.
No error is created in the Error Bucket for the 400 HTTP responses.
|
As was mentioned above, the response from the DDI always include a further clarification on the error in the body of the response.
9.3. 500 Server error
This means that an unexpected error occurred in the DDI while processing the request. The DDI does not save the sent transaction and the transaction has to be resent.
Upon receiving 500 HTTP response the supplier has to attempt sending the transaction again with one second delay between the second retry.
And then doubling the delay for every next request if 500 HTTP response is returned.
In total the supplier should make 10 attempts.
-
First call with a
500response back -
Wait 1 second
-
Second call with a
500response back -
Wait 2 seconds
-
Third call with a
500response back -
Wait 4 seconds
-
..
-
Tenth call with a
500response back
If the DDI still responds 500 after the last retry, the supplier has to create a ticket to Danske Spil (see section Contact Information).
Obviously it is enough with one single ticket when multiple transactions are failing.
No need to create a separate ticket for every failed transaction.
The failed transactions should not be resent until Danske Spil resolves the issue.
10. Delivering files
10.1. Reporting Frequency
The following requirements for reporting frequency applies to a normal data flow, where no errors have been identified during the Data Validation. If errors have been identified in the data, the requirements stated in section Error handling must be followed.
- Transaction Structures or Session Structures
-
All transaction structures must be sent to the DDI no later than 3 minutes after a transaction has occurred or a session has ended. A transaction structure is any structure related to a specific transaction or session.
-
End of Day
All EndOfDay messages must be sent to the DDI no later than 03:57 (UTC) with data covering the previous day.
-
Start-, EndOfGame-, and End-Structures
All Start- EndOfGame-, and End-Structures, must be sent to the DDI immediately after the step, which the relevant data structure is related to, has happened. E.g. after starting a game, the game supplier must immediately send the corresponding StartStructure to the DDI.
-
10.2. Specific Game Requirements
This section contains details for the specific game type reporting. Sections Tests, Errors, Tickets apply to all games.
10.2.1. Bingo
DGA have these requirements when reporting jackpots.
Reporting Schemas
The game supplier can report in the following formats:
| XML | JSON |
|---|---|
KasinospilPrSessionStrukturType |
CasinoGamePrSessionStructure |
JackpotUdloesningStrukturType |
JackpotReleaseStructure |
EndOfDayRapportStrukturType |
EndOfDayReportStructure |
CasinoGamePrSessionStructure){
"LicenseHolderAndGame": {
"GameCategoryName": "KasinospilSinglePlayer"
},
"CasinoGameAggregatedPrSessionList": [
{
"CasinoGameSession": {
"GameProductName": "Just another Casino game",
"GameProductOpenNetwork": 0,
"PlayerInformationIdentification": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"GameTransactionIdentification": "xxxxxxxxx_xxxxx_xxxxx_xxxxxxxxx",
"GamePurchaseDateTime": "2019-07-09T11:30:12.44Z",
"GameActualEndDateTime": "2019-07-09T12:09:01.297Z",
"GameSaleschannel": "Mobil",
"CasinoGameCategory": "spilleautomat",
"CasinoGameDepositGame": 500.00,
"CasinoGameWinningsGame": 420.00,
"CasinoGameNumberOfDraws": 80,
"CurrencyInformationCode": "DKK",
"GamePlace": {
"GameHomepage": "www.danskepsil.dk"
},
"CoincidenceGeneratorList": [
{
"CoincidenceGenerator": {
"CoincidenceGeneratorIdentification": "RandomGeneratorId",
"CoincidenceGeneratorSoftwareId": "RandomGeneratorSoftware"
}
}
],
"JackpotList": [
{
"Jackpot": {
"JackpotIdentification": "Super_Casino_Jackpot_17193219",
"CasinoGameDepositJackpot": 1.0
}
}
]
}
}
]
}
JackpotReleaseStructure{
"LicenseHolderAndGame": {
"GameCategoryName": "KasinospilSinglePlayer"
},
"Jackpot": {
"JackpotIdentification": "Super_Casino_Jackpot_17193219",
"JackpotDateTime": "2019-07-19T11:42:45.249Z",
"JackpotTotalWinning": 50000,
"JackpotCommissionRake": 400.50,
"CurrencyInformationCode": "DKK",
"PlayerList": [
{
"Player": {
"PlayerInformationIdentification": "xxxxxxxxxxxxxxxxxxxx",
"JackpotWinnings": 25000.0
}
},
{
"Player": {
"PlayerInformationIdentification": "xxxxxxxxxxxxxxxxxxxx",
"JackpotWinnings": 25000.0
}
}
]
}
}
Unique identification of the jackpot. This attribute is the connection to the report, which is done, when the jackpot has been won. It is therefore important, that the same JackpotIdentification is used in the CasinoGamePrSessionStructure and the JackpotReleaseStructure, which is reported, when the jackpot has been won.
10.2.2. Tests
To achieve certification the following steps must be successfully tested:
-
DGA test
-
Review
-
Datastructure review
-
List of errors review
-
-
Resending functionality
-
Replacement of invalid
EndOfDay(EOD) -
Cancellation of Transaction / Session
-
Resettlement (only relevant for
FixedOddsEndStructure)
-
-
Duplicate checks
-
Duplicate
transactionIdError -
Duplicate
SpilTransaktionIdentifikationGameTransactionIdentification
-
-
-
Load test
-
Recovery test
-
End-to-end test
10.2.3. Errors
See Error handling
10.2.4. Tickets
On creating tickets for Team SAFE - See Creating a ticket (Manual error handling)
10.3. Transaction Reporting Isolation
When reporting transactions, each transaction is stored and delivered to the DDI. The End of Day report should be calculated in the supplier Backend Datastore. As a supplier, you can not sum upon transactions passing through the DDI, since that will always match. You need to calculate End of Day in the Backend Datastore to take care of any unreported transactions.
11. Error handling
This section describes how the DDI identifies errors, and how the Game Supplier should handle these errors. The error handling is based on the type of error that has occurred.
Errors can be reported in two ways:
-
API Errors: If the API directly throws an error.
-
API call return HTTP code = 4xx or 5xx.
-
Data is rejected by the DDI.
-
Error description is received in the body of the HTTP request response (together with the HTTP status code of 4xx or 5xx)
-
-
Error queue: If data is accepted by API, but subsequent processing later detects an error (e.g. EOD error).
-
API call return HTTP code = 200.
-
Request is initially accepted by the DDI, but error detected at a later processing step. The value of Severity in Error Definitions in the error queue response determines if the message was accepted or not.
-
Error description will be available via the ErrorQueue.
-
11.1. Process for error handling
The process for error handling is as follows:
| Step | Description |
|---|---|
1 |
The game supplier submits data to the API. |
2A |
Data can be declined directly by the API if the data does not validate to the appropriate schema and does not pass other simple error checks. In that case, a direct error response is issued to the game supplier via the HTTP connection (4xx/Error). The data is deleted. |
2B |
Data can be accepted by the API. In that case, a direct accept response is issued to the game supplier via the HTTP connection (200/OK). The data is then controlled by the Advanced Controls. |
3A |
If no errors are found, the data is sent to SAFE. Process ends. |
3B |
If any error is found, an error is created in the ErrorBucket. |
4 |
The game supplier pulls errors from the ErrorBucket. |
5A |
Depending on the type of error the error must be handled appropriately. In any case the error should be corrected. |
5B |
Depending on the agreement with DS Operations, the game supplier must either create a ticket, contact DS Operations directly, or (in a few cases) fix the issue without contact to DS. In general it is recommended to include DS; The agreement with DS Operations should always be followed. |
6 |
When the error is corrected, a ResendFlag is added to the data message before submitting it to the API again (go to step 1). See section Resend data |
11.2. API Errors
A complete list of error that can be returned by the DDI is available in table Error Definitions.
Errors that will be caught by the API (step 2A in Process for error handling above) will result in rejection of the sent data and an error response from the API.
Rejection implies that the data, the {transactionId}, and the {sequenceId} have not reached Danske Spil.
Therefore, the data message must be resent in a corrected state, but the same {transactionId} and {sequenceId}
can be used.
The error response from the API includes errorMessage, errorType, and stackTrace in a json object (example
below). The json object will be found in the body of the API response.
{
"errorMessage": "ERR4-Authentication Error - invalid API Key, invalid SupplierID",
"errorType": "Meta data error",
"stackTrace": [ "An array containing the stack trace from the server side execution" ]
}
11.3. Error Queue Errors
The game supplier can receive various error messages from the error queue depending on the type of error that has occurred.
Errors which will be caught by the Advanced Controls (step 3B in Process for error handling above) will result in an entry in the ErrorBucket.
The ErrorBucket should be pulled by the game supplier in defined intervals. [api-resources-2] is used for pulling the ErrorBucket (see section API resources).
Error messages from the ErrorBucket are returned to the game supplier in a json object. The below example contains
a single error message
{
"results": [
{
"errorid": "c6537392-68f8-11e6-8b77-86f30ca893d3", // UUID (time-based)
"transactionid": "EX-1451649600-9d988a9170b7a9fd",
"sequenceid": "12345678-1",
"receivedatetime": "1451650700", // Unix timestamp in seconds
"errorcategory": 1, // Reference to ErrorCategory in ListOfErrors
"errortype": 2, // Reference to ErrorType in ListOfErrors
"errornumber":"1-2", // Reference to ErrorNumber in ListOfErrors
"errordescription" : "This is a human readable description of the Error", // Optional
"errormessage": "Description/Exception from control tool",
"suggestedcorrection": "(re)send data / recalculate" // Optional
}
]
}
All fields marked with Optional can be omitted from an error message.
If a field is not relevant, or there is no information to show, it will be marked with Null.
E.g. any Missing SequenceId error will have the fields {transactionId} + {sequenceId} set to Null, as these fields are not relevant for the Missing SequenceId error.
{
"results": [
{
"ErrorID": "c6537392-68f8-11e6-8b77-86f30ca893d3", // UUID
"TransactionID": "Null",
"SequenceID": "Null",
"Receive datetime": "1451650700", // Unix timestamp in seconds
"ErrorCategory": 1, // Reference to ErrorCategory in ListOfErrors
"ErrorType": 2, // Reference to ErrorType in ListOfErrors
"ErrorNumber": "1-2", // Reference to ErrorNumber in ListOfErrors
"ErrorDescription": "This is a human readable description of the Error", // Optional
"ErrorMessage": "Description/Exception from control tool",
"SuggestedCorrection": "(re)send data / recalculate" // Optional
}
]
}
Errors are grouped into error categories:
| Category | # | Description |
|---|---|---|
SchemaValidation |
1 |
Schema validation and syntax errors. |
DuplicatedData |
3 |
Deep inspection of content detected illegally duplicated data. |
Authentication |
4 |
Authentication failure. |
Internal |
5 |
DDI internal error, that should be investigated by Danske Spil. |
ReportingSummary |
7 |
End-of-day / End-of-game summary verification errors. |
DataValidation |
24 |
Deep inspection of game data breaks validation rule. |
Request |
10 |
API Request error, could be bad path parameter, query parameter or http header. |
The severity of the error determines if the message was accepted or rejected.
| Severity | Description |
|---|---|
SUCCESS |
Only used for API calls, indicates that the submission passed initial verification, but it may be rejected later on. |
WARNING |
The transaction was accepted by the system, but processing revealed that there is an issue that must be fixed. An example could be that an end-of-game calculation did not match. |
ERROR |
Transaction was rejected. Investigate what went wrong, correct the issue and send it again. |
Complete list of errors defined by the system.
| # | Id | Severity | Source | Description |
|---|---|---|---|---|
1-1 |
SchemaValidationError |
|
API & Error Queue |
Schema validation failed. This means that the data sent (in request body or as a large-file) did not conform to the schema. |
1-5 |
IllegalCurrencyCode |
|
API & Error Queue |
A purchase or end control report containing an unsupported currency code was submitted. Current list if supported currency codes is: [DKK, USD] |
1-10 |
EmptyBodyError |
|
API & Error Queue |
The request body or large-file was empty. |
1-60 |
JsonToXmlConversionError |
|
API & Error Queue |
An error occurred while converting JSON to XML. A common cause for this error, is that the ordering of fields in JSON does not match the requirements of the XML schema. |
1-91 |
CharacterEncodingError |
|
API & Error Queue |
The body/file could not be converted into a proper UTF-8 string, you should double check that you are sending data in UTF-8 encoding without a BOM. |
1-92 |
JsonSyntaxError |
|
API & Error Queue |
The submitted JSON could not be processed due to a JSON syntax error. |
1-93 |
XmlSyntaxError |
|
API & Error Queue |
The submitted XML could not be processed due to an XML syntax error. |
3-1 |
DuplicateRequestTransactionIdError |
|
API & Error Queue |
The given TransactionID has previously been reported |
3-2 |
DuplicateGameTransactionIdError |
|
API & Error Queue |
Same purchase has previously been reported. |
3-4 |
ShouldHaveBeenSentAsReplacementError |
|
Error Queue |
The given EndControlId has previously been reported |
3-5 |
IncompleteReplacementError |
|
Error Queue |
The given EndControlId has previously been reported |
5-90 |
UnhandledExceptionError |
|
API & Error Queue |
This is a generic error used for unexpected errors, it is most likely caused by a bug in the DDI and should be resolved in corporation with the SAFE team. |
5-95 |
ReplayError |
|
API |
This is a generic error used for replay issues. |
5-99 |
ServiceWindow |
|
API |
DDI is down due to system maintenance. |
7-1 |
EndOfDayError |
|
Error Queue |
An EndOfDay report does not match reported transactions. |
7-2 |
EndOfGameError |
|
Error Queue |
An EndOfGame report does not match reported transactions. |
10-6 |
LargeFileNotOnSFTPError |
|
API |
When a large-file request is submitted, but the large-file cannot be found on the SFTP server. |
10-23 |
InvalidParameterError |
|
API |
An API call request parameter (path, query, header) was invalid. |
10-24 |
AuthorizationError |
|
API |
An error occurred while calling the API, this is most likely due to invalid credentials |
24-1 |
ResendWithoutOriginal |
|
Error Queue |
A replacement is attempted but the original could not be found. |
24-2 |
FlowCheckError |
|
Error Queue |
If a transaction-, end-of-game- or end-structure was received, but no corresponding structure with the same game product identification has been received. |
24-9 |
MissingEndOfDayStructure |
|
Error Queue |
An expected EndOfDay report has not been received before 04:00 the day after reporting date. |
24-10 |
ResendAlreadyReplacedError |
|
Error Queue |
A replacement is attempted, but the transaction has already been replaced |
24-11 |
ResendWithChangedGameTypeError |
|
Error Queue |
A replacement is attempted, but the GameType of the replacement is different from the original |
24-12 |
ResendQuarantinedError |
|
Error Queue |
A replacement is attempted, but the transaction is in quarantine and awaiting approval |
24-66 |
EndStructureWinnerMismatch |
|
Error Queue |
Mismatch detected between DrawEndStructure and NumberGameEndStructure. The DrawEndStructure contains a list of winner numbers which were not all found in NumberGameEndStructure. |
11.4. Creating a ticket (Manual error handling)
When a game supplier encounters a problem, which they cannot solve themselves, or they pull an error from the ErrorBucket, they should inform DS operations. Contact should be through the agreed channels, which can be ticket system (e.g. JIRA), email, or similar.
The game supplier should send the following information to DS operations (minimum requirements):
-
Ticket ID (from game supplier ticket system) – if relevant
-
SupplierID
-
Detailed description of the issue
-
How the error was provoked
-
Date when the issue was provoked
It is important that the Game Supplier is as detailed as possible when explaining the issue, so that the error is identified and fixed effectively.
When the issue progresses in the ticket system, the game supplier should keep DS informed.
11.5. Resend data
When a game supplier needs to correct data that has been sent to DDI, the resend parameter must be used. There are three different categories for resending data:
BASE-URL/{format}/{gameCategory}/view/{gameType}/{supplier}/{sequenceId}/{transactionId}?resend=cancel
BASE-URL/{format}/{gameCategory}/view/{gameType}/{supplier}/{sequenceId}/{transactionId}?resend=resettle
BASE-URL/{format}/{gameCategory}/view/{gameType}/{supplier}/{sequenceId}/{transactionId}?resend={transactionId}
The type of structure being sent determines which category the resend falls into (see below). Valid values for the resend parameter are:
-
cancel– indicates a cancellation of a transaction -
resettle– indicates a resettlement of a transaction -
replace- A valid{transactionId}– indicates a replacement for the transaction with the given{transactionId}. It must be the{transactionId}from the messages that need to be replaced. See a previous section for details on the format of a{transactionId}.
11.5.1. Cancellations
Purchase transaction can be cancelled either with or without a replacement transaction.
-
A cancellation is performed by filling out the relevant transaction structure with exactly the same information as the originally reported purchase transaction, and adding the attribute
{GameCancellation}[SpilAnnullering] containing the value 1 and adding the attribute{GameCancellationDateTime}[SpilAnnulleringDatoTid] containing the date and time of the cancellation. -
The attribute
{GameTransactionIdentification}[SpilTransaktionIdentifikation] is used as a key to connect the purchase transaction to the cancellation transaction. As such, it is imperative that the same value is reported in this attribute, both for the cancellation and the original transaction. -
The End of Day report must not be regenerated if a cancellation is made.
-
Perform step 1 and 2 from Transaction cancellation without a replacement transaction..
-
Send the corrected purchase transaction The corrected transaction must contain a new value in the attribute
{GameTransactionIdentification}[SpilTransaktionIdentifikation]
Structures that allow for and must be resent as a cancellation are displayed below:
| JSON Structures | XML Structures |
|---|---|
|
[FastOddsTransaktionStruktur] |
|
[KasinospilPrSessionStruktur] |
|
[PokerCashGamePrSessionStruktur] |
|
[PokerTurneringTransaktionStruktur] |
|
[PuljespilTransaktionStruktur] |
|
[MonopolTalspilTransaktionStruktur] |
|
[MonopolBingoNetskrabTransaktionStruktur] |
|
[MonopolVirtuelFastOddsTransaktionStruktur] |
|
[MonopolVirtuelFastOddsSlutStruktur] |
11.5.2. Resettlement
Resettlement are only relevant for the game suppliers who deliver data from Fixed Odds games.
-
In a situation where a result for a football game has been incorrectly registered in the gaming system and the winnings of one or several bets must be changed, then the difference in winnings must be reported in a new
{FixedOddsEndStructure}[FastOddsEndStruktur].-
E.g if a player initially has won 100 DKK and this amount has been reported in a
{FixedOddsEndStructure}, but after the correction of the result, the player should not have won, the value of -100 DKK for the player should be reported in a new{FixedOddsEndStructure}. The same value for{GameTransactionIdentification}[SpilTransaktionIdentifikation] should be reported, as this value is used to connect different wins for the player on the game and calculate the total win for the player on this game. A new value of Game{GameActualEndDateTime}[SpilFaktiskSlutDatoTid] must be reported which matches the day and time where the resettlement was performed.
-
-
The End of Day report should not be regenerated if a resettlement is used.
-
E.g. If a
{FixedOddsEndStructure}[FastOddsSlutStruktur] is reported on day 1 and another{FixedOddsEndStructure}[FastOddsSlutStruktur] as a resettlement is reported on day 2, then the original must be a part of the End of Day report from day 1 and the resettlement must be part of the End of Day report on day 2.
-
Structures that allows for and must be resent as a resettlement are filled in below:
| JSON Structure | XML Structure |
|---|---|
|
|
|
|
11.5.3. Replacement
Replacement of data must only be done in situations, where the game supplier has by mistake reported incorrect/wrong data. Replacements require a manual approval. This will be communicated via an email to the operation of Danske Spil.
-
When replacing data through the DDI:
-
The resend parameter must be set to the
{transactionId}of the transaction that is to be replaced:resend={transactionId} -
The replacement must replace a structure of the same type
-
The new and correct message must have a new
{transactionId}and{sequenceId}, as normal.
-
-
The End Of Day report should not be regenerated if a game supplier uses a replacement, since structures that allow for a replacement are not reported in the End of Day report. However, replacements can be used to replace an End of Day report in situations where the data, which the End of Day report is based on, is wrong. Re-generation of End of Day reports must be agreed upon on a case-by-case basis.
Structures that allows for and must be resent as a replacement are filled in below:
| JSON Structure | XML Structure |
|---|---|
|
[PuljespilStartStrukturType] |
|
[ManagerspilStartStrukturType] |
|
[PokerTurneringStartStrukturType] |
|
[EndOfDayRapportStrukturType] |
|
[PuljespilEndOfGameStrukturType] |
|
[PokerTurneringSlutStrukturType] |
|
[PuljespilSlutStrukturType] |
|
[ManagerspilSlutStrukturType] |
|
[JackpotUdloesningStrukturType] |
|
[MonopolBingoNetskrabStartStrukturType] |
|
[MonopolEndOfDayRapportStrukturType] |
|
[MonopolFysiskSkrabSlutStrukturType] |
|
[MonopolFysiskSkrabStartStrukturType] |
|
[MonopolFysiskSkrabTransaktionStrukturType] |
|
[MonopolLandeDataStrukturType] |
|
[MonopolLodtraekningSlutStrukturType] |
|
[MonopolLodtraekningStartStrukturType] |
|
[MonopolTalspilEndOfGameStrukturType] |
|
[MonopolTalspilSlutStrukturType] |
|
[MonopolTalspilStartStrukturType] |
11.5.4. Manual
Other types of errors are handled from case to case in dialogue with the DGA. These types of errors could be corrupt files, validation errors and the like.
11.6. Examples
11.6.1. Calling the error bucket with a UUID
Yes the errorId param is a time-based version 1 UUID and you can e.g. get all Playtech errors from a specific date.
Example on getting all Playtech errors from 2020-06-24T00:00:00.00Z (ISO8601 format) Example on creating a time-based version 1 UUID in Java
Instant date = Instant.parse("2020-06-24T00:00:00.00Z");
UUID errorIdTimeBased2020_06_24 = UUIDs.startOf(date.toEpochMilli())
The errorIdTimeBased2020_06_24 will have the value a5920000-b5ad-11ea-8080-808080808080 and can be used like this:
HTTP GET:
/errors/Playtech/a5920000-b5ad-11ea-8080-808080808080
Response contain only errors from the 2020-06-24 and later:
{
"responseMessage": "Retrieving error list from after errorid.",
"results": [
{
"errormessage": "Playtech25-1592968475-08c2213df260accc|Playtech|MonopolEndOfDayRapportStrukturType|EOD_2020-06-16_MonopolVirtuelFastOdds_Fodbold|DKK. Validation failed for: 'EndOfDayRapportIndskudSpil' (reported sum: 9332, calculated from transactions: 0, diff: -9332), 'EndOfDayRapportAntalSpil' (reported sum: 9332, calculated from transactions: 0, diff: -9332), 'EndOfDayRapportGevinster' (reported sum: 844.6700000, calculated from transactions: 0, diff: -844.6700000)",
"errortype": 1,
"errordescription": "End of day report does not match with delivered data",
"receivedatetime": 1592969671,
"errorid": "9d562b00-b5cb-11ea-957c-1f270d7ac33d",
"errornumber": "7-1",
"transactionid": "Playtech25-1592968475-08c2213df260accc",
"errorcategory": 7,
"suggestedcorrection": "Resend data"
},
...
11.7. Error Bucket - test phase
As a supplier, you need to monitor the error bucket to discover trends in errors. Should you not understand the error message in the trending error or disagree that this is an error, please create a jira ticket to Team Safe.
12. Contact Information
Below administrative information regarding SAFE/DDI will be stated.
12.1. How are new versions distributed?
New versions will be distributed when available to listed contact persons. To ensure inclusion on the distribution list please contact operations@danskespil.dk.
12.2. How do you request changes to the documentation?
Changes to the documentation can be send to operations@danskespil.dk. Please provide the information requested in the below table.
Submitter of change request
Company:
Name:
E-mail:
| Id of change request | Page no. | Section no. | Description | Comments |
|---|---|---|---|---|
12.3. How is a Supplier ID obtained?
The {supplier} must be agreed upon with Danske Spil to ensure uniqueness. Please contact operations@danskespil.dk to obtain a {supplier}.
12.4. How is a Vertical ID obtained?
The {verticalId} must be agreed upon with Danske Spil to ensure uniqueness. Please contact operations@danskespil.dk to obtain a {verticalId}.
12.5. How is a API-Key obtained?
The API-Key must be designed by Danske Spil to ensure uniqueness. Please contact operations@danskespil.dk to obtain a API-Key.
13. Glossary
| Term | Definition |
|---|---|
Danish Gambling Authority |
Government gambling market regulator under Danish Ministry of Taxation. |
DDI |
Data Delivery Interface. An interface used by game suppliers to report game data. |
DGA |
See Danish Gambling Authority. |
EOD |
End-of-day. |
EOG |
End-of-game. |
Error Bucket |
An API to retrieve errors related to the submitted gaming data. |
Large files |
Payloads above 1 MB have to uploaded to SFTP server first. And then a reference to the uploaded file has to be sent to DDI. |
SAFE (also "safe", "Safe") |
Part of Danske Spil responsible for development and maintenance of DDI. |
SAFE2 |
See SAFE. |
SFTP |
SSH File Transfer Protocol. However, in the context of integration to DDI it means sending "large files". See "Large files". |
Small files |
Payloads that do no exceed 1 MB |
SPM |
Spillemyndigheden. See Danish Gambling Authority. |
Standard records |
Specifications made by the Danish Gambling Authority on the gambling data reporting. |
Supplier |
In the context of this document it is a supplier of a game data that is integrating to DDI. |
14. Appendix
14.1. Available schemas
When clicking the DGA link a page refresh can be needed to jump to the pdf page.
| XSD | JSON | Purpose |
|---|---|---|
EndOfDayRapportStrukturType |
EndOfDayReportStructure |
This structure contains summarized game data from the game supplier per calendar day. Info from DGA p. 117 |
FastOddsSlutStrukturType |
FixedOddsEndStructure |
This structure is for end reporting of fixed odds betting. Info from DGA p. 12 |
FastOddsTransaktionStrukturType |
FixedOddsTransactionStructure |
This structure is for reporting of fixed odds games. Info from DGA p. 9 |
HestDKEventTotalStrukturType |
HorseDKEventTotalStructure |
This structure can be used as a combined report of horse races, this structure contain information from HorseDKEventStartStructure, HorseDKStartStructure and HorseDKEndStructure, hence it eliminates the need of these reports. Info from DGA p. 71 |
HestDKTransaktionStrukturType |
HorseDKTransactionStructure |
This structure is used for reporting horse racing. Info from DGA p. 76 |
HesteagtigEventTotalStrukturType |
HorselikeEventTotalStructure |
This structure can be used as a combined report of horselike races, this structure contain information from HorselikeEventStartStructure, HorselikeStartStructure and HorselikeEndStructure, hence it eliminates the need of these reports. Info from DGA p. 71 |
HesteagtigTransaktionStrukturType |
HorselikeTransactionStructure |
This structure must be reported as a summary when the horselike racing game has been ended. Info from DGA p. 76 |
JackpotUdloesningStrukturType |
JackpotReleaseStructure |
This structure must be reported per jackpot when a jackpot has been won. Info from DGA p. 112 |
KasinospilPrSessionStrukturType |
CasinoGamePrSessionStructure |
This structure is for reporting casino games including online bingo. Info from DGA p. 15 |
ManagerspilSlutStrukturType |
ManagerGameEndStructure |
This structure is for end reporting of a manager game. Info from DGA p. 66 |
ManagerspilStartStrukturType |
ManagerGameStartStructure |
This structure is for start reporting of a manager game. Info from DGA p. 60 |
ManagerspilTransaktionStrukturType |
ManagerGameTransactionStructure |
This structure is for reporting player transactions (buy-in or cancellation) of a manager game. Info from DGA p. 62 |
MonopolBingoNetskrabStartStrukturType |
BingoNetScratchStartStructure |
This structure is for reporting the start of a bingo/netscratch game. (See p. 9 in "Krav til rapportering af spil - Monopol v2.6.doc") |
MonopolBingoNetskrabTransaktionStrukturType |
BingoNetScratchTransactionStructure |
This structure is for reporting buy-in and winnings of a bingo/netscratch game. (See p. 9 in "Krav til rapportering af spil - Monopol v2.6.doc") |
MonopolEndOfDayRapportStrukturType |
EndOfDayReportStructure |
This structure contains summarized game data from the game supplier per calendar day. Info from DGA p. 117 |
MonopolFysiskSkrabSlutStrukturType |
PhysicalScratchEndStructure |
This structure is for reporting the end of a scratch game, when vouchers can no longer be cashed in. (See p. 9 in "Krav til rapportering af spil - Monopol v2.6.doc") |
MonopolFysiskSkrabStartStrukturType |
PhysicalScratchStartStructure |
This structure is for reporting the start of a scratch game. (See p. 9 in "Krav til rapportering af spil - Monopol v2.6.doc") |
MonopolFysiskSkrabTransaktionStrukturType |
PhysicalScratchTransactionStructure |
This structure is for reporting the daily sales of a scratch game. (See p. 9 in "Krav til rapportering af spil - Monopol v2.6.doc") |
MonopolJackpotUdloesningStrukturType |
JackpotReleaseStructure |
This structure must be reported per jackpot when a jackpot has been won. |
MonopolLandeDataStrukturType |
MonopolyCountriesData |
This structure is for reporting a game with international pools and the pools have been calculated. (See p. 10 in "Krav til rapportering af spil - Monopol v2.6.doc") |
MonopolLodtraekningSlutStrukturType |
DrawEndStructure |
This structure is for reporting a lottery and the winners have been identified. (See p. 8 in "Krav til rapportering af spil - Monopol v2.6.doc") |
MonopolLodtraekningStartStrukturType |
DrawStartStructure |
This structure is for reporting starting a lottery creating the candidate list. (See p. 8 in "Krav til rapportering af spil - Monopol v2.6.doc") |
MonopolLodtraekningTotalStrukturType |
DrawTotalStructure |
This structure is for reporting a Classlottery and the winners have been identified. (See p. 8 in "Krav til rapportering af spil - Monopol v2.6.doc") |
MonopolTalspilEndOfGameStrukturType |
NumberGameEndOfGameStructure |
This structure is for reporting end-of-game for a pool game. (See p. 7 in "Krav til rapportering af spil - Monopol v2.6.doc") |
MonopolTalspilSlutStrukturType |
NumberGameEndStructure |
This structure is for reporting the end of a number game. (See p. 7 in "Krav til rapportering af spil - Monopol v2.6.doc") |
MonopolTalspilStartStrukturType |
NumberGameStartStructure |
This structure is for reporting the start of a number game. (See p. 7 in "Krav til rapportering af spil - Monopol v2.6.doc") |
MonopolTalspilTransaktionStrukturType |
NumberGameTransactionStructure |
This structure is for reporting player transactions of a number game. (See p. 7 in "Krav til rapportering af spil - Monopol v2.6.doc") |
MonopolVirtuelFastOddsSlutStrukturType |
VirtualFixedOddsEndStructure |
This structure is for reporting when a virtual fixed odds game have been settled. |
MonopolVirtuelFastOddsTransaktionStrukturType |
VirtualFixedOddsTransactionStructure |
This structure is used for reporting a virtual fixed odds purchase or a cancellation of a previous purchase. |
PokerCashGamePrSessionStrukturType |
PokerCashGamePrSessionStructure |
This structure is for reporting poker cash games. Info from DGA p. 22 |
PokerTurneringSlutStrukturType |
PokerTournamentEndStructure |
This structure is for reporting poker tournament is finished. Info from DGA p. 33 |
PokerTurneringStartStrukturType |
PokerTournamentStartStructure |
This structure is for reporting poker tournament is set up. Info from DGA p. 27 |
PokerTurneringTransaktionStrukturType |
PokerTournamentTransactionStructure |
This structure is for reporting transactions (buy-in and cancellation) for poker tournaments. Info from DGA p. 29 |
PuljespilEndOfGameStrukturType |
PoolGameEndOfGameStructure |
This structure is for reporting pool games when the pool game is closed for taking stakes. Info from DGA p. 50 |
PuljespilSlutStrukturType |
PoolGameEndStructure |
This structure is for reporting pool games when the pool game has ended and a winners have been found. Info from DGA p. 54 |
PuljespilStartStrukturType |
PoolGameStartStructure |
This structure is for reporting pool games when the pool game is set up. Info from DGA p. 41 |
PuljespilTransaktionStrukturType |
PoolGameTransactionStructure |
This structure is for reporting pool game transactions (player stakes and cancellation). Info from DGA p. 45 |
| Game Qualifier | Games | Schema |
|---|---|---|
Bingo Game |
Bingo |
JackpotUdloesningStrukturType, EndOfDayRapportStrukturType, KasinospilPrSessionStrukturType |
Casino Game |
Casino, Live Casino, Bingo, Spillehjørnet |
JackpotUdloesningStrukturType, EndOfDayRapportStrukturType, KasinospilPrSessionStrukturType |
Fixed Odds |
Tips |
PuljespilTransaktionStrukturType, PuljespilStartStrukturType, PuljespilEndOfGameStrukturType, PuljespilSlutStrukturType |
Horselike Game |
Dantoto |
HesteagtigTransaktionStrukturType, HesteagtigEventTotalStrukturType, HestDKEventTotalStrukturType, HestDKTransaktionStrukturType |
Net Scratch Game |
Quick |
MonopolFysiskSkrabTransaktionStrukturType, MonopolFysiskSkrabSlutStrukturType, MonopolEndOfDayRapportStrukturType, MonopolFysiskSkrabStartStrukturType |
Number Game |
Lotto, Viking Lotto, Eurojackpot, Alt eller Intet, Keno |
MonopolTalspilStartStrukturType, MonopolTalspilEndOfGameStrukturType, MonopolEndOfDayRapportStrukturType, MonopolLodtraekningSlutStrukturType, MonopolTalspilSlutStrukturType, MonopolLodtraekningStartStrukturType, MonopolTalspilTransaktionStrukturType |
Physical Scratch Game |
Quick |
MonopolFysiskSkrabTransaktionStrukturType, MonopolFysiskSkrabSlutStrukturType, MonopolEndOfDayRapportStrukturType, MonopolFysiskSkrabStartStrukturType |
Poker Cash Game |
Poker |
PokerTurneringTransaktionStrukturType, EndOfDayRapportStrukturType, PokerTurneringStartStrukturType, PokerTurneringSlutStrukturType, PokerCashGamePrSessionStrukturType |
Poker Tournament |
Poker |
PokerTurneringTransaktionStrukturType, EndOfDayRapportStrukturType, PokerTurneringStartStrukturType, PokerTurneringSlutStrukturType, PokerCashGamePrSessionStrukturType |
Pool Game |
Oddset, eOddset |
FastOddsTransaktionStrukturType, FastOddsSlutStrukturType, EndOfDayRapportStrukturType |
Virtual Fixed Odds |
None |