Cross-Border Transfers and Breach Readiness: Cloud Regions, LLM Providers, Standard Contracts and the 72-Hour Notification
A transfer abroad is not only where the server is; logs, backups, e-mail, analytics and LLM prompts are transfers too, and since 2024 they must rely on an adequacy decision, an appropriate safeguard such as a standard contract, or an occasional case. On Bookstore we keep the sub-processor inventory in code, check the five-business-day notification of standard contracts at start-up, and use an access log to get a breach's scope and the 72-hour deadline within the first hour.
Version note: The code in this post was built and run with .NET 10, ASP.NET Core 10, Entity Framework Core 10.0.15 (SQLite) and xUnit 2.9. Legal dates and periods were checked against mevzuat.gov.tr, the Official Gazette and kvkk.gov.tr on 15 November 2026.
This series is a developer's view, not legal advice. Which mechanism a transfer relies on, the type of standard contract and whether an incident counts as a data breach are decided by your legal counsel; the dates and contract types in this post are examples.
Guess First
In a meeting about Bookstore, the online bookstore used as the shared example across this site's series, someone says "we have no transfers abroad, our servers are in Turkey". The API's appsettings.json, however, holds the addresses of the telemetry, e-mail and analytics services, plus a newly added LLM service for book recommendations. How many transfers abroad are there? The second question: on Friday night, just after 01:00, the password of a support account is stolen and the account exports customer records. The incident is noticed on Saturday morning at 09:30. When is the last moment to notify the Board, and can you answer "who was affected" within an hour?
The Transfer Regime Since 1 June 2024
Article 9 of KVKK, Turkey's Law No. 6698, was rewritten by Law No. 7499 and took effect on 1 June 2024 (we went through the whole change in post 01). The procedure is in the Regulation on the Procedures and Principles for the Transfer of Personal Data Abroad; it was published in Official Gazette No. 32598 of 10 July 2024 and took effect on the day of publication. The structure has three steps:
- Adequacy decision: the Board may decide that a country, a sector in a country or an international organisation offers adequate protection; the decision is published in the Official Gazette and on the Authority's website and is reviewed at least every four years (Regulation Articles 8 and 9).
- Appropriate safeguards, when there is no adequacy decision and provided the data subject can exercise their rights in the destination country too: an agreement between public bodies that is not an international treaty, binding corporate rules, a standard contract or a written undertaking (Article 10). All except the standard contract need the Board's permission or approval.
- Occasional transfer, when neither of the above applies: listed cases such as the data subject's explicit consent after being informed of the risks, or the performance of a contract. The regulation defines occasional as irregular, happening once or a few times, not continuous and not part of the ordinary course of business (Article 16).
The sentence that matters most to a developer is in the definitions: under Article 4(1)(e) of the regulation, a transfer means sending the data to a controller or processor abroad or making it accessible to them in any other way. So even if the data sits in a database in Turkey, a support team abroad with remote access to that database raises a transfer question too.
The Board adopted the standard contracts with decision No. 2024/959 of 4 June 2024, and the Authority announced four types on 10 July 2024: controller to controller, controller to processor, processor to processor and processor to controller. The rules that turn into code are in Article 14 of the regulation: the text is used without changes; it is notified to the Authority within five business days of the signatures being completed, physically, by registered electronic mail or by another method the Board sets; if the parties did not agree who notifies, the exporter does; and changes to the parties or the content, and the end of the contract, are notified the same way. The five business days are also in Article 9(5) of the Law, and a separate administrative fine provision was added for failing to notify (Article 18(1)(d)); follow the amounts on the Authority's website. On the GDPR side, the counterpart is the standard data protection clauses adopted by the Commission in Article 46(2)(c); Article 46(2) lists them among safeguards that need no specific authorisation from a supervisory authority.
The Technical Impact: Where the Data Goes
"Where is the server" is only one of the places the data goes. The services a customer record touches in Bookstore and the location of each:

On the cloud side, the region choice decides where the application and the database live; but geo-redundant backups can go to a second region, and the logs of managed services can go wherever the provider puts them. We covered regions and availability zones in the Cloud Fundamentals series post "What Is the Cloud? IaaS, PaaS, SaaS, Serverless, Shared Responsibility, Regions and Availability Zones", and the backup options of managed databases in post 08 of the same series. The telemetry backend, the e-mail and SMS provider, the CDN and the WAF are each a separate sub-processor. Analytics is a special case: the data never passes through Bookstore's server and goes straight from the visitor's browser to the provider; the consent from post 06 does not remove this transfer, it only decides when it starts.
The Sub-Processor Inventory in Code
In post 01 we left the list of sub-processors to this post. The reason to keep it in code rather than in a spreadsheet is the same as in post 02: it changes with the code and is verified by tests.
// Mechanisms of Law No. 6698 Article 9 and the 2024 transfer regulation.
public enum TransferMechanism { Domestic, AdequacyDecision, StandardContract, BindingCorporateRules, WrittenUndertaking, Occasional }
public sealed record SubProcessor(
string Key,
string Role,
string Location,
bool Abroad,
string[] DataCategories,
TransferMechanism Mechanism,
string? ContractType = null,
DateOnly? SignedOn = null,
DateOnly? NotifiedOn = null);
When the API starts, TransferInventoryCheck looks at three things: is every external service in the configuration in the inventory; does every entry abroad rely on a valid mechanism; was every standard contract notified within five business days:
case TransferMechanism.StandardContract:
DateOnly due = BusinessDays.Add(s.SignedOn!.Value, NotificationBusinessDays, holidays.Days);
if (s.NotifiedOn is not { } notified || notified > due)
{
problems.Add($"{s.Key}: standard contract signed {s.SignedOn:yyyy-MM-dd}, notification due {due:yyyy-MM-dd}, notified {s.NotifiedOn?.ToString("yyyy-MM-dd") ?? "never"}");
}
break;
The team building book recommendations added ExternalServices:Llm:Endpoint to appsettings.json but did not touch the inventory. The API did not start (path shortened):
Unhandled exception. System.InvalidOperationException: Transfer inventory: Llm: configured (https://llm.example.net) but not in the sub-processor inventory
at Program.<Main>$(String[] args) in .../src/Bookstore.Api/Program.cs:line 40
Failing at start-up may look harsh, but the transfer starts with the first request in code; the contract and the notification must come before it. Once the entry was added to the inventory, the API started. The lab command prints a summary of the inventory:
cd tools/Bookstore.PrivacyLab
dotnet run -- transfers
cloud-hosting abroad StandardContract customer-profile, order-billing, sign-in-session, payment, consent
telemetry abroad StandardContract telemetry
email abroad StandardContract customer-profile
analytics abroad StandardContract analytics-events
llm abroad StandardContract purchase-history-titles
payment-provider domestic Domestic payment
iys domestic Domestic commercial-message
The tests also check the notification period and the mechanism. For a contract signed on Thursday 1 October 2026 the five business days end on 8 October; a notification made on 9 October is reported in the test output as sms: standard contract signed 2026-10-01, notification due 2026-10-08, notified 2026-10-09. Official holidays come from configuration, as in post 06. The dates in the inventory are examples; the legal team owns the real ones.
Prompts Sent to LLM Providers
Every prompt sent to an LLM API is a transfer, together with the personal data in it. Bookstore's recommendation feature puts only the titles of the books the customer bought into the prompt; no name, e-mail or address. That is why the category in the inventory is purchase-history-titles. We covered what goes into a prompt, and the data that leaks from the system prompt and the RAG context, in the LLM Application Security series post "Sensitive Data and Hidden Context Exposure: System Prompts, RAG Context and Data Sent to Providers"; the canary test from post 05 works for a prompt builder too. What to check in the contract: how long the provider keeps prompts, whether it uses them to train models and which region processes them.
A common shortcut here is "we only send a request now and then, so it counts as an occasional transfer". A feature that runs on every recommendation request is part of the ordinary course of business; by the regulation's definition it is not occasional. A continuous service marked as Occasional in the inventory fails the test with a running service cannot rely on an occasional transfer.
Data Breaches: 72 Hours and the People Affected
Article 12(5) of the Law requires the controller to notify the data subject and the Board as soon as possible when personal data is obtained by others through unlawful means. With decision No. 2019/10 of 24 January 2019, the Board interpreted "as soon as possible" as 72 hours. These are the points of the decision that turn into code and runbooks.
The period starts when the controller learns of the breach; notification is made without delay and within 72 hours at the latest, using the Board's Personal Data Breach Notification Form. If it is late for a justified reason, the reasons for the delay are explained as well; if not all information is available at once, it is provided in stages.
Once the people affected are identified, they are notified within the shortest reasonable time: directly if their contact address is known, otherwise by other suitable means such as a notice on the controller's website.
A processor notifies the controller of a breach on its side without delay. The details of breaches, their effects and the measures taken are recorded, and a data breach response plan is prepared and reviewed at regular intervals.
The Board set the minimum content of the notice to data subjects with decision No. 2019/271 of 18 September 2019: in clear and plain language, when the breach happened, which data was affected, separating personal data from special categories of personal data, the possible consequences, the measures taken or proposed to reduce the harm, and a contact person or channels for more information. In the GDPR, Article 33 requires notifying the supervisory authority within 72 hours but not when the breach is unlikely to result in a risk to people; Article 34 ties notifying data subjects to a high risk. Article 12(5) of KVKK sets no such threshold; which incident is a breach to be notified is the legal team's call, but in both cases the code should answer the same questions quickly.
Preparation in Code: Access Log, Impact Query, Notice Draft
The answer to "who was affected" cannot be written after the breach; it needs a record kept before it. Support, export and back office endpoints write a row every time they access personal data:
// Who read whose personal data, and which category. Written by support, export and back office endpoints.
[DataCategory("access-audit")]
public sealed class PersonalDataAccess
{
[NoDataClassification]
public long Id { get; set; }
[PersonalData(InformationType = "Staff ID")]
public required string ActorId { get; set; }
[PersonalData(InformationType = "Customer ID")]
public int CustomerId { get; set; }
[NoDataClassification]
public required string DataCategory { get; set; }
[NoDataClassification]
public required string Action { get; set; }
[NoDataClassification]
public DateTime AtUtc { get; set; }
}
BreachScope lists the affected customers with their categories for an account and a time window, computes the last moment to notify the Board and splits the categories into personal and special categories, as 2019/271 asks. None of Bookstore's categories is special category data under Article 6 of the Law, but the notice draft states the distinction explicitly. The lab command builds the scenario from the opening question: the staff-17 account exports the profiles of three customers between 01:10 and 01:40 and looks at the billing details of two of them; the incident is noticed at 09:30:
dotnet run -- breach
detected: 2026-11-14 09:30 UTC
notify Authority by 2026-11-17 09:30 UTC
affected customers: 3
customer 1: customer-profile
customer 3: customer-profile, order-billing
customer 4: customer-profile, order-billing
reachable by e-mail: 3
When: between 2026-11-14 01:10 and 2026-11-14 01:40 UTC
Personal data affected: customer-profile, order-billing
Special category data affected: none
Possible consequences: [legal team]
Measures taken and recommended: [security and legal team]
Contact: kvkk@example.com
The answer to the opening question: the 72 hours do not wait for the weekend; for a breach learned of on Saturday at 09:30 the last moment is Tuesday at 09:30. Code cannot fill the open lines of the draft; consequences and measures need an assessment. The code's job is to give the people making that assessment the right list and the deadline within the first hour. Without the access log, the only honest answer would be "every customer the support account could reach".
Production Notes
The access log is a target for the attacker too: whoever takes over the support account may try to erase their tracks. Write the log to a separate store the application can write to but not delete from (an append-only table, a separately authorised log account or immutable storage), and keep times in UTC. The log itself is personal data; it is in the inventory as access-audit and has a retention period. We will cover the whole incident response process (detection, scoping, preserving evidence, communication) in the Threat Modeling and Secure Development Process series; this post's part is having the queries ready for the first hour of that process. The notification form, the text template for customers and the contact details should be ready in advance and rehearsed in a tabletop exercise at least once a year. The sub-processor inventory is part of breach readiness too: the contract should require the processor to report a breach to you without delay, and the inventory should show which provider holds which category.
Common Pitfalls
The most common one is saying "the server is in Turkey" while logs, backups, the e-mail service or analytics are abroad. The second is not noticing that geo-redundant backups are copied to a region in another country. The third is signing a standard contract and forgetting to notify it within five business days, or changing its text. The fourth is basing a continuously running integration on an occasional transfer. The fifth is keeping no access log and being unable to narrow the scope when a breach happens. The sixth is counting the 72 hours from the wrong moment, for example when the investigation ends or when the office opens on Monday.
Sources for This Post
Accessed on 15 November 2026.
- Law No. 6698, Articles 9, 12 and 18:
mevzuat.gov.tr/mevzuatmetin/1.5.6698.pdf; transfer regulation:resmigazete.gov.tr/eskiler/2024/07/20240710-2.htmandmevzuat.gov.tr/MevzuatMetin/yonetmelik/7.5.40863.pdf - Board decision 2024/959 and the standard contracts:
kvkk.gov.tr/Icerik/7938; transfers page:kvkk.gov.tr/Icerik/2053 - Board decision 2019/10 and the breach notification form:
kvkk.gov.tr/Icerik/5362; Board decision 2019/271:kvkk.gov.tr/Icerik/5547 - GDPR Articles 33, 34 and 46:
eur-lex.europa.eu/eli/reg/2016/679/oj
A Small Experiment
★☆☆ Move the notification date of the email entry to six business days after signing and see which message breaks both the test and the API start-up. Then add 29 October to the holiday list and repeat the experiment in that week.
★★☆ Add a filter to the support team's customer view endpoint (the masked view from post 03) that writes a PersonalDataAccess row, and test that the row is written even when the request itself fails.
★★★ Add a second mode to BreachScope: a stolen database backup instead of a stolen account. List the customers and categories as of the moment the backup was taken, taking the erasure ledger from post 04 into account, and test that customers erased after the backup still appear in the affected list.
The series ends here. In seven posts we built the part of KVKK and the GDPR that turns into code on Bookstore: recognising and inventorying personal data, classifying fields and protecting minimisation with tests, encrypting sensitive fields at the application level, erasing and anonymising what has passed its retention period, keeping personal data out of logs and telemetry, collecting consent in a provable way, and knowing where the data goes so that the right questions can be answered in the first hour of a breach. What they all share is that the legal team's decisions are written down somewhere in the code and guarded by a test.