diff --git a/README.md b/README.md
index 68d1f57..1810d90 100644
--- a/README.md
+++ b/README.md
@@ -1,9 +1,39 @@
-
AMP Client
+
AMP Client PHP
+
:mega: PHP Client for Advertising Management Platform
+
+
+
+
+
+
+
+
## Installation
```sh
$ composer require 68publishers/amp-client
```
+
+## Versions compatibility matrix
+
+| PHP client version | PHP version | AMP version | API version |
+|:----------------------:|-------------|:----------------:|:-----------:|
+| `^1.0` | `>=7.4` | `>=2.12` | `1` |
+
+
+## Integration without a framework
+
+The client is not dependent on any framework and can therefore be used independently.
+For standalone use, continue to the [Integration without a framework](docs/integration-without-framework.md) section.
+
+## Integration with Nette Framework
+
+The client is well integrated into the Nette framework.
+For documentation, continue to the [Integration with Nette framework](docs/integration-with-nette-framework.md) section.
+
+## License
+
+@todo
diff --git a/docs/integration-with-nette-framework.md b/docs/integration-with-nette-framework.md
new file mode 100644
index 0000000..6837eda
--- /dev/null
+++ b/docs/integration-with-nette-framework.md
@@ -0,0 +1,175 @@
+# Integration with Nette framework
+
+For more information on how the client works, we also recommend reading the [Integration without a framework](./integration-without-framework.md) section.
+
+## Client integration
+
+The minimum configuration is as follows:
+
+```neon
+extensions:
+ amp_client: SixtyEightPublishers\AmpClient\Bridge\Nette\DI\AmpClientExtension
+
+amp_client:
+ url:
+ channel:
+```
+
+The only mandatory values in the configuration are the AMP application URL and the channel (project) name.
+Here are all the configuration options:
+
+```neon
+extensions:
+ amp_client: SixtyEightPublishers\AmpClient\Bridge\Nette\DI\AmpClientExtension
+
+amp_client:
+ url:
+ channel:
+ # Http method, allowed values are GET (default) and POST
+ method: GET
+ # Locale for requests (null by default):
+ locale: en
+ # AMP API version:
+ version: 1
+ # Default resources for all requests:
+ default_resources:
+ category:
+ - 1
+ # Value for http header X-Amp-Origin.
+ origin: https://www.example.com
+
+ cache:
+ # Cache store, by default null (cache is disabled):
+ storage: @Nette\Caching\Storage
+ # Expiration must be set for caching:
+ expiration: '1 hour'
+ # Overrides Cache-Control header in responses from the AMP:
+ cache_control_header_override: 'max-age=60'
+
+ http:
+ # Custom Guzzle options:
+ guzzle_config: []
+
+ renderer:
+ # "phtml" or "latte". The bridge is automatically resolved. If you are working on standard Nette application the bridge will be always "latte"
+ bridge: latte
+ # Here can be overriden the default templates for each position type:
+ templates:
+ single: %appDir%/templates/amp/single.latte
+ random: %appDir%/templates/amp/random.latte
+ multiple: %appDir%/templates/amp/multiple.latte
+ not_found: %appDir%/templates/amp/not_found.latte
+```
+
+Two important services are now available in the DI Container - `AmpClientInterface` and `RendererInterface`.
+You can autowire them into, for example, Presenter or any service:
+
+```php
+use Nette\Application\UI\Presenter;
+use SixtyEightPublishers\AmpClient\AmpClientInterface;
+use SixtyEightPublishers\AmpClient\Renderer\RendererInterface;
+
+final class MyPresenter extends Presenter {
+ public function __construct(
+ private readonly AmpClientInterface $client,
+ private readonly RendererInterface $renderer,
+ ) {
+ parent::__construct();
+ }
+
+ public function actionDefault(): void {
+ $request = new BannersRequest([
+ new Position('homepage.top'),
+ new Position('homepage.promo', [
+ new BannerResource('role', 'guest'),
+ ]),
+ ]);
+
+ $response = $this->client->fetchBanners($request);
+
+ bdump($this->renderer->render($response->getPosition('homepage.top')));
+ bdump($this->renderer->render($response->getPosition('homepage.promo')));
+ }
+}
+```
+
+## Latte macros integration
+
+Banners can be rendered directly from the Latte template without having to manually call the client. We need to register another extension for this:
+
+```neon
+extensions:
+ amp_client.latte: SixtyEightPublishers\AmpClient\Bridge\Nette\DI\AmpClientLatteExtension(%debugMode%)
+```
+
+Now we have the macro `{banner}` available in the application, and we can use it in templates:
+
+```latte
+{banner homepage.top}
+{banner homepage.promo, ['role' => 'guest']}
+```
+
+Banners are now requested via API and rendered to the template automatically.
+
+Each `{banner}` macro makes a separate request to the AMP API, so in our example above, two requests are sent.
+This can be solved by the following configuration:
+
+```neon
+amp_client.latte:
+ rendering_mode: queued_in_presenter_context # the default value is "direct"
+```
+
+Now when rendering a page via `nette/application`, information about all banners to be rendered is collected and a request to the AMP API is sent only once the whole template is rendered.
+The banners are then inserted back into the rendered page. This behavior also works automatically with AJAX snippets.
+
+### Configuring client before the first fetch
+
+Occasionally, we may want to configure the client before making a request to the AMP API from the template.
+For example, we left the `locale` blank in the main `neon` configuration and want to set it up at runtime.
+To do this, we can use a custom service implementing the `ConfigureClientEventHandlerInterface` interface.
+
+```php
+use SixtyEightPublishers\AmpClient\Bridge\Latte\Event\ConfigureClientEvent;
+use SixtyEightPublishers\AmpClient\Bridge\Latte\Event\ConfigureClientEventHandlerInterface;
+
+final class SetupLocaleEventHandler implements ConfigureClientEventHandlerInterface
+{
+ public function __construct(
+ private readonly MyLocalizationService $localizationService,
+ ) {}
+
+ public function __invoke(ConfigureClientEvent $event): ConfigureClientEvent
+ {
+ $client = $event->getClient();
+ $config = $client->getConfig();
+
+ return $event->withClient(
+ $client->withConfig(
+ $config->withLocale($this->localizationService->getCurrentLocale()),
+ ),
+ );
+ }
+}
+```
+
+And register it:
+
+```neon
+services:
+ - SetupLocaleEventHandler
+ # or
+ -
+ autowired: self
+ type: SetupLocaleEventHandler
+```
+
+Our handler will be called before the first AMP API call from the Latte.
+
+### Renaming the macro
+
+Macro `{banner}` can be renamed. The following configuration will rename it to `{ampBanner}`.
+
+```neon
+amp_client.latte:
+ banner_macro_name: ampBanner
+```
diff --git a/docs/integration-without-framework.md b/docs/integration-without-framework.md
new file mode 100644
index 0000000..de317d0
--- /dev/null
+++ b/docs/integration-without-framework.md
@@ -0,0 +1,271 @@
+# Integration without a framework
+
+## Client initialization
+
+The client is simply instanced as follows:
+
+```php
+use SixtyEightPublishers\AmpClient\AmpClient;
+use SixtyEightPublishers\AmpClient\ClientConfig;
+
+$config = ClientConfig::create('', '');
+$client = AmpClient::create($config);
+```
+
+The only mandatory values in the configuration are the AMP application URL and the channel (project) name.
+Other optional options are as follows:
+
+```php
+use SixtyEightPublishers\AmpClient\AmpClient;
+use SixtyEightPublishers\AmpClient\ClientConfig;
+use SixtyEightPublishers\AmpClient\Request\ValueObject\BannerResource;
+
+$config = ClientConfig::create('', '');
+
+# Configure http method, allowed values are GET (default) and POST.
+$config = $config->withMethod('POST');
+
+# Configure locale for requests (null by default).
+$config = $config->withLocale('en');
+
+# Configure AMP API version.
+$config = $config->withVersion(1);
+
+# Configure default resources for all requests.
+$config = $config->withDefaultResources([
+ new BannerResource('category', ['1']),
+]);
+
+# Configure value for http header X-Amp-Origin.
+$config = $config->withOrigin('https://www.example.com');
+
+# Configure http cache. More about the cache in the documentation below.
+$config = $config->withCacheExpiration('1 hour');
+$config->withCacheControlHeaderOverride('max-age=60');
+
+$client = AmpClient::create($config);
+```
+
+> :exclamation: Please note that `ClientConfig` is immutable, just like the other client classes.
+
+### Cache
+
+By default, the client uses [NoCacheStorage](../src/Http/Cache/NoCacheStorage.php), so requests are not cached.
+This can be changed by setting the cache and its expiration:
+
+```php
+use SixtyEightPublishers\AmpClient\AmpClient;
+use SixtyEightPublishers\AmpClient\ClientConfig;
+use SixtyEightPublishers\AmpClient\Http\Cache\InMemoryCacheStorage;
+
+$config = ClientConfig::create('', '')
+ ->withCacheExpiration('1 hour');
+
+$client = AmpClient::create($config)
+ ->withCacheStorage(new InMemoryCacheStorage());
+```
+
+The cache expiration can be set using the DateTime modifier (for example `2 hours`, `1 day` etc.) or an integer that specifies the number of seconds for which the cache should be stored.
+Currently, the following storages are implemented:
+
+- [InMemoryCacheStorage](../src/Http/Cache/InMemoryCacheStorage.php)
+- [NetteCacheStorage](../src/Bridge/Nette/NetteCacheStorage.php)
+
+By default, the cache is controlled by the `Cache-Control` and `ETag` headers that AMP sends in the response.
+However, the `Cache-Control` header can be overridden in the configuration:
+
+```php
+$config = $config->withCacheControlHeaderOverride('no-cache');
+```
+
+This setting will cache the responses, but a response is revalidated before each use.
+The directives that are processed are `no-store`, `no-cache`, `max-age` and `s-maxage`. More information about the `Cache-Control` header [here](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control).
+
+### Custom Guzzle options
+
+The client sends requests using Guzzle. If you would like Guzzle to give the client default options, you must instantiate the AMP client with the HTTP client factory.
+
+```php
+use SixtyEightPublishers\AmpClient\AmpClient;
+use SixtyEightPublishers\AmpClient\ClientConfig;
+use SixtyEightPublishers\AmpClient\Http\HttpClientFactory;
+use SixtyEightPublishers\AmpClient\Response\Hydrator\ResponseHydrator;
+use SixtyEightPublishers\AmpClient\Response\Hydrator\BannersResponseHydratorHandler;
+
+$guzzleConfig = [
+ # ... guzzle options ...
+];
+
+$config = ClientConfig::create('', '');
+$client = AmpClient::create(
+ config: $config,
+ httpClientFactory: new HttpClientFactory(
+ responseHydrator: new ResponseHydrator([
+ new BannersResponseHydratorHandler(),
+ ]),
+ guzzleClientConfig: $guzzleConfig,
+ ),
+);
+```
+
+## Fetching banners
+
+```php
+use SixtyEightPublishers\AmpClient\AmpClientInterface;
+use SixtyEightPublishers\AmpClient\Request\BannersRequest;
+use SixtyEightPublishers\AmpClient\Request\ValueObject\Position;
+use SixtyEightPublishers\AmpClient\Request\ValueObject\BannerResource;
+
+/** @var AmpClientInterface $client */
+
+$request = new BannersRequest([
+ new Position('homepage.top'),
+ new Position('homepage.promo', [
+ new BannerResource('role', 'guest'),
+ ]),
+]);
+
+$response = $client->fetchBanners($request); # SixtyEightPublishers\AmpClient\Response\BannersResponse
+
+$homepageTop = $response->getPosition('homepage.top');
+$homepagePromo = $response->getPosition('homepage.promo');
+```
+
+## Rendering banners
+
+Banners can be rendered simply by using the `Renderer` class:
+
+```php
+use SixtyEightPublishers\AmpClient\AmpClientInterface;
+use SixtyEightPublishers\AmpClient\Renderer\Renderer;
+use SixtyEightPublishers\AmpClient\Response\BannersResponse;
+
+/** @var BannersResponse $response */
+
+$renderer = Renderer::create();
+
+echo $renderer->render($response->getPosition('homepage.top'));
+```
+
+The default templates are written as `.phtml` templates and can be found [here](../src/Renderer/Phtml/Templates). Templates can be also overwritten:
+
+```php
+use SixtyEightPublishers\AmpClient\Renderer\Renderer;
+use SixtyEightPublishers\AmpClient\Renderer\Phtml\PhtmlRendererBridge;
+use SixtyEightPublishers\AmpClient\Renderer\Templates;
+
+$bridge = new PhtmlRendererBridge();
+$bridge = $bridge->overrideTemplates(new Templates([
+ Templates::TemplateSingle => '/my_custom_template_for_single_position.phtml',
+]));
+
+$renderer = Renderer::create($bridge);
+```
+
+The following template types can be overwritten:
+
+```php
+use SixtyEightPublishers\AmpClient\Renderer\Templates;
+
+new Templates([
+ Templates::TemplateSingle => '/single.phtml', # for positions with the display type "single"
+ Templates::TemplateMultiple => '/multiple.phtml', # for positions with the display type "multiple"
+ Templates::TemplateRandom => '/random.phtml', # for positions with the display type "random"
+ Templates::TemplateNotFound => '/notFound.phtml', # for positions that were not found
+])
+```
+
+### Rendering banners using Latte
+
+Banners can also be rendered using the [Latte](https://github.com/nette/latte) templating system.
+Versions `^2.11` and `^3.0` are supported.
+
+```php
+use SixtyEightPublishers\AmpClient\AmpClientInterface;
+use SixtyEightPublishers\AmpClient\Renderer\Renderer;
+use SixtyEightPublishers\AmpClient\Renderer\Latte\LatteRendererBridge;
+use SixtyEightPublishers\AmpClient\Response\BannersResponse;
+use SixtyEightPublishers\AmpClient\Renderer\Latte\ClosureLatteFactory;
+use Latte\Engine;
+
+/** @var BannersResponse $response */
+
+$renderer = Renderer::create(
+ LatteRendererBridge::fromEngine(new Engine()),
+);
+
+# or lazily via
+
+$renderer = Renderer::create(
+ new LatteRendererBridge(
+ new ClosureLatteFactory(function (): Engine {
+ return new Engine();
+ }),
+ ),
+);
+
+echo $renderer->render($response->getPosition('homepage.top'));
+```
+
+The default `.latte` templates are located [here](../src/Renderer/Latte/Templates) and can be overridden in the same way as the default `.phtml` templates.
+
+## Latte templating system integration
+
+In addition to being able to render banners manually using Latte templates, the client offers the ability to render them directly using a custom Latte macro.
+The macro is registered as follows:
+
+```php
+use SixtyEightPublishers\AmpClient\AmpClientInterface;
+use SixtyEightPublishers\AmpClient\Renderer\RendererInterface;
+use SixtyEightPublishers\AmpClient\Bridge\Latte\AmpClientLatteExtension;
+use SixtyEightPublishers\AmpClient\Bridge\Latte\RendererProvider;
+use Latte\Engine;
+
+/** @var AmpClientInterface $client */
+/** @var RendererInterface $renderer */
+
+$engine = new Engine();
+$provider = (new RendererProvider($client,$renderer))
+ ->setDebugMode(true); # exceptions from Client and Renderer are suppressed in non-debug mode
+
+AmpClientLatteExtension::register($engine, $provider);
+
+$engine->render(__DIR__ . '/template.latte');
+```
+
+```latte
+{* ./template.latte *}
+
+{banner homepage.top}
+{banner homepage.promo, ['role' => 'guest']}
+```
+
+Banners are now requested via API and rendered to the template automatically.
+
+Each `{banner}` macro makes a separate request to the AMP API, so in our example above, two requests are sent.
+This can be solved, however you need to render the Latte to a text string, not a buffer.
+
+```php
+use SixtyEightPublishers\AmpClient\AmpClientInterface;
+use SixtyEightPublishers\AmpClient\Renderer\RendererInterface;
+use SixtyEightPublishers\AmpClient\Bridge\Latte\AmpClientLatteExtension;
+use SixtyEightPublishers\AmpClient\Bridge\Latte\RendererProvider;
+use SixtyEightPublishers\AmpClient\Bridge\Latte\RenderingMode\QueuedRenderingMode;
+use Latte\Engine;
+
+/** @var AmpClientInterface $client */
+/** @var RendererInterface $renderer */
+
+$engine = new Engine();
+$provider = (new RendererProvider($client,$renderer))
+ ->setDebugMode(true) # exceptions from Client and Renderer are suppressed in non-debug mode
+ ->setRenderingMode(new QueuedRenderingMode());
+
+AmpClientLatteExtension::register($engine, $provider);
+
+$output = $engine->renderToString(__DIR__ . '/template.latte');
+
+echo $provider->renderQueuedPositions($output);
+```
+
+Now the client requests both banners in the template with one request.