Danish Games Logo

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:

  1. Start structure definition schema

  2. Transaction structure definition schema

  3. End-of-day structure definition schema

  4. End-of-game structure definition schema

  5. 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:

  1. A new number game draw (lottery) is created, typically with a duration of 1 week. This requires a Start structure to be reported.

  2. Customers can buy tickets to the draw. And for every purchase a Transaction structure has to be sent.

  3. 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.

  4. 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.

ddi interface overview
Figure 1. DDI interface

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

Data submission endpoint with placeholders
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.

Data submission endpoint with placeholders
GET /SAFE/errors/{supplier}/{errorId} HTTP/1.1 (1)
1 Same as with the data submission supplier is the id of the supplier
An example retrieving all error messages
GET /SAFE/errors/MySupplier HTTP/1.1
An example retrieving error messages after error a5920000-b5ad-11ea-8080-808080808080
GET /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

supplier

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.

errorId

The unique UUID

Optional query parameters:

Parameter Description

ticketId

The supplier’s ticket id

resolution

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.

Data submission endpoint with placeholders
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}
An example of a 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.

Table 1. DDI environments
Environment API host SFTP host Purpose

Test default

api.test.safe.dsservice.eu

sftp.test.safe.dsservice.eu

Test default

Town 23

town23.api.test.safe.dsservice.eu

sftp.test.safe.dsservice.eu

Town 23

Town 31

town31.api.test.safe.dsservice.eu

sftp.test.safe.dsservice.eu

Town 31

Town 33

town33.api.test.safe.dsservice.eu

sftp.test.safe.dsservice.eu

Town 33

Town 35

town35.api.test.safe.dsservice.eu

sftp.test.safe.dsservice.eu

Town 35

Town 36

town36.api.test.safe.dsservice.eu

sftp.test.safe.dsservice.eu

Town 36

Town 42

town42.api.test.safe.dsservice.eu

sftp.test.safe.dsservice.eu

Town 42

Town 43

town43.api.test.safe.dsservice.eu

sftp.test.safe.dsservice.eu

Town 43

Town 46

town46.api.test.safe.dsservice.eu

sftp.test.safe.dsservice.eu

Town 46

Town 51

town51.api.test.safe.dsservice.eu

sftp.test.safe.dsservice.eu

Town 51

Town 52

town52.api.test.safe.dsservice.eu

sftp.test.safe.dsservice.eu

Town 52

Town 53

town53.api.test.safe.dsservice.eu

sftp.test.safe.dsservice.eu

Town 53

Town 54

town54.api.test.safe.dsservice.eu

sftp.test.safe.dsservice.eu

Town 54

Production

api.safe.dsservice.eu

sftp.safe.dsservice.eu

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.

Using test environment to retrieve all errors for 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 request path with query parameters with placeholders
POST /SAFE/{payloadType}/{gameCategory}/view/{gameType}/{supplier}/{sequenceId}/{transactionId}?{queryParameters} HTTP/1.1
POST request path with query parameters example
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.

Sending small file XML payload to DDI
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

Submitting EndOfDayReportStructure in json for supplier MySupplier to test environment
POST /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

Submitting FastOddsTransaktionStruktur in xml for supplier MySupplier to test environment
POST /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.

An example of a public SSH key
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQDSNM6RVVmwN3y0NurIQnmZgjcx5K5zzZu9nDqopW4JIn/mr8OYZI3heSJShnIM8EThvwVGXXXyyJVRQAvRHYFO4DxS6bufSNWr3BxBGaGYlYxI9mgvQnT6+MzE3oZyEMdQNPlV5VfbileXlrPoAk1TkGdVdhwdLJMI2B4KUyMf+Q==

A step by step connection guide for one environment:

  1. Generate a private / public RSA key pair for the environment

  2. Send the public key to Danske Spil and inform for which environment this should be set up for

  3. Receive a username for the environment from Danske Spil

  4. 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.

Uploaded file path on the SFTP server
/incoming/{yyyyMM}/{transactionId}.{payloadType}
Example of an uploaded file path in 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.

  1. queryParameters in the request have to include a parameter large=yes.

  2. The Content-Type header of the request always have to be application/json.

  3. The body of the request has to have a pointer to the uploaded SFTP file in a format of { "path": "/{yyyyMM}/{transactionId}.{payloadType}" }.

Sending a large file pointer to the DDI
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
Example of sending a large xml file pointer to the DDI
POST /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
Example of sending a large json file pointer to the DDI
POST /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
<root>
    <element>
        value
    </element>
</root>
{
  "element": "  value \n \t "
}
<root>
    <element>value</element>
</root>

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.

  • payloadType xml

    • gameCategory monopol

      • /SAFE/xsd/monopol/view/MonopolEndOfDayRapportStrukturType

    • gameCategory other

      • /SAFE/xsd/other/view/EndOfDayRapportStrukturType

  • payloadType json

    • gameCategory monopol

      • /SAFE/json/monopol/view/EndOfDayReportStructure

    • gameCategory other

      • /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:

  • 200 OK

  • 4xx Client error

  • 5xx Server error

The response always includes an additional information in the body.

Example of a successful data submission (200 HTTP response)
{
    "responseCode": "SUCCESS",
    "responseMessage": "Validation succeeded, message accepted for further processing.",
    "responseID": "17dcbf0b-7857-4b4c-89f7-fe18b6ea9b31"
}
Example of an unsuccessful data submission (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 500 response back

  • Wait 1 second

  • Second call with a 500 response back

  • Wait 2 seconds

  • Third call with a 500 response back

  • Wait 4 seconds

  • ..

  • Tenth call with a 500 response 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:

Table 2. Schemas used in Bingo
XML JSON

KasinospilPrSessionStrukturType

CasinoGamePrSessionStructure

JackpotUdloesningStrukturType

JackpotReleaseStructure

EndOfDayRapportStrukturType

EndOfDayReportStructure

Example of correctly reported jackpot (Example of a 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
            }
          }
        ]
      }
    }
  ]
}
Example of 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:

  1. DGA test

    1. Review

      1. Datastructure review

      2. List of errors review

    2. Resending functionality

      1. Replacement of invalid EndOfDay (EOD)

      2. Cancellation of Transaction / Session

      3. Resettlement (only relevant for FixedOddsEndStructure)

    3. Duplicate checks

      1. Duplicate transactionId Error

      2. Duplicate SpilTransaktionIdentifikation GameTransactionIdentification

  2. Load test

  3. Recovery test

  4. End-to-end test

10.2.3. Errors

10.2.4. Tickets

On creating tickets for Team SAFE - See Creating a ticket (Manual error handling)

10.3. Transaction Reporting Isolation

messages isolation
Figure 2. Transaction Reporting

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:

  1. 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)

  2. 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

error handling process
Figure 3. Error handling process

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.

response.json
{
  "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

error.json
{
  "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.

error-sequence.json
{
  "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:

Table 3. 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.

Table 4. Error Severities
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.

Table 5. Error Definitions
# Id Severity Source Description

1-1

SchemaValidationError

ERROR

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

ERROR

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

ERROR

API & Error Queue

The request body or large-file was empty.

1-60

JsonToXmlConversionError

ERROR

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

ERROR

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

ERROR

API & Error Queue

The submitted JSON could not be processed due to a JSON syntax error.

1-93

XmlSyntaxError

ERROR

API & Error Queue

The submitted XML could not be processed due to an XML syntax error.

3-1

DuplicateRequestTransactionIdError

ERROR

API & Error Queue

The given TransactionID has previously been reported

3-2

DuplicateGameTransactionIdError

ERROR

API & Error Queue

Same purchase has previously been reported.

3-4

ShouldHaveBeenSentAsReplacementError

ERROR

Error Queue

The given EndControlId has previously been reported

3-5

IncompleteReplacementError

ERROR

Error Queue

The given EndControlId has previously been reported

5-90

UnhandledExceptionError

ERROR

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

ERROR

API

This is a generic error used for replay issues.

5-99

ServiceWindow

ERROR

API

DDI is down due to system maintenance.

7-1

EndOfDayError

WARNING

Error Queue

An EndOfDay report does not match reported transactions.

7-2

EndOfGameError

WARNING

Error Queue

An EndOfGame report does not match reported transactions.

10-6

LargeFileNotOnSFTPError

ERROR

API

When a large-file request is submitted, but the large-file cannot be found on the SFTP server.

10-23

InvalidParameterError

ERROR

API

An API call request parameter (path, query, header) was invalid.

10-24

AuthorizationError

ERROR

API

An error occurred while calling the API, this is most likely due to invalid credentials

24-1

ResendWithoutOriginal

ERROR

Error Queue

A replacement is attempted but the original could not be found.

24-2

FlowCheckError

WARNING

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

Error Queue

An expected EndOfDay report has not been received before 04:00 the day after reporting date.

24-10

ResendAlreadyReplacedError

ERROR

Error Queue

A replacement is attempted, but the transaction has already been replaced

24-11

ResendWithChangedGameTypeError

ERROR

Error Queue

A replacement is attempted, but the GameType of the replacement is different from the original

24-12

ResendQuarantinedError

ERROR

Error Queue

A replacement is attempted, but the transaction is in quarantine and awaiting approval

24-66

EndStructureWinnerMismatch

WARNING

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:

Cancellation
BASE-URL/{format}/{gameCategory}/view/{gameType}/{supplier}/{sequenceId}/{transactionId}?resend=cancel
Resettlements
BASE-URL/{format}/{gameCategory}/view/{gameType}/{supplier}/{sequenceId}/{transactionId}?resend=resettle
Replacements
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.

Transaction cancellation without a replacement transaction.
  1. 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.

  2. 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.

  3. The End of Day report must not be regenerated if a cancellation is made.

Transaction cancellation with a replacement transaction.
  1. Perform step 1 and 2 from Transaction cancellation without a replacement transaction..

  2. 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

FixedOddsTransactionStructure

[FastOddsTransaktionStruktur]

CasinoGamePrSessionStructure

[KasinospilPrSessionStruktur]

PokerCashGamePrSessionStructure

[PokerCashGamePrSessionStruktur]

PokerTournamentTransactionStructure

[PokerTurneringTransaktionStruktur]

PoolGameTransactionStructure

[PuljespilTransaktionStruktur]

NumberGameTransactionStructure

[MonopolTalspilTransaktionStruktur]

BingoNetScratchTransactionStructure

[MonopolBingoNetskrabTransaktionStruktur]

VirtualFixedOddsTransactionStructure

[MonopolVirtuelFastOddsTransaktionStruktur]

VirtualFixedOddsEndStructure

[MonopolVirtuelFastOddsSlutStruktur]

11.5.2. Resettlement

Resettlement are only relevant for the game suppliers who deliver data from Fixed Odds games.

  1. 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].

    1. 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.

  2. The End of Day report should not be regenerated if a resettlement is used.

    1. 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

FixedOddsEndStructure

FastOddsSlutStrukturType

VirtualFixedOddsEndStructure

MonopolVirtuelFastOddsSlutStrukturType

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.

  1. When replacing data through the DDI:

    1. The resend parameter must be set to the {transactionId} of the transaction that is to be replaced: resend={transactionId}

    2. The replacement must replace a structure of the same type

    3. The new and correct message must have a new {transactionId} and {sequenceId}, as normal.

  2. 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

PoolGameStartStructure

[PuljespilStartStrukturType]

ManagerGameStartStructure

[ManagerspilStartStrukturType]

PokerTournamentStartStructure

[PokerTurneringStartStrukturType]

EndOfDayReportStructure

[EndOfDayRapportStrukturType]

PoolGameEndOfGameStructure

[PuljespilEndOfGameStrukturType]

PokerTournamentEndStructure

[PokerTurneringSlutStrukturType]

PoolGameEndStructure

[PuljespilSlutStrukturType]

ManagerGameEndStructure

[ManagerspilSlutStrukturType]

JackpotReleaseStructure

[JackpotUdloesningStrukturType]

BingoNetScratchStartStructure

[MonopolBingoNetskrabStartStrukturType]

EndOfDayReportStructure

[MonopolEndOfDayRapportStrukturType]

PhysicalScratchEndStructure

[MonopolFysiskSkrabSlutStrukturType]

PhysicalScratchStartStructure

[MonopolFysiskSkrabStartStrukturType]

PhysicalScratchTransactionStructure

[MonopolFysiskSkrabTransaktionStrukturType]

MonopolyCountriesData

[MonopolLandeDataStrukturType]

DrawEndStructure

[MonopolLodtraekningSlutStrukturType]

DrawStartStructure

[MonopolLodtraekningStartStrukturType]

NumberGameEndOfGameStructure

[MonopolTalspilEndOfGameStrukturType]

NumberGameEndStructure

[MonopolTalspilSlutStrukturType]

NumberGameStartStructure

[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

errorbucket test
Figure 4. 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.

Table 6. Schemas
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

Table 7. Game Schema Usage
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